第九屆編程大賽預選賽#題解

教育場了屬於是,深感自己變菜了


YYDS

題意:

按照字典序排序輸入的字元串,後面加上 YYDS! 後輸出

題解:

  • 使用sort排序字元串,sort默認為字典序
  • 使用set容器,默認對容器內元素採用字典序排序

打卡題,這裡就不放程式碼了


TCL和TQL

題意:

如果有一個60以下則輸出「TCL」,如果所有課程的成績都大於等於85,且平均分大於等於90,則輸出”TQL”

題解:

可以使用printf格式化輸出,其他的直接看程式碼吧

程式碼:

    for(int i=1;i<=n;i++){
        int m,sum=0,flag=1,a;
        cin>>m;
        for(int j=0;j<m;j++){
            cin>>a;//輸入成績
            if(flag==-1)continue;//若已經存在60以下科目則不繼續判斷
            if(a<60)flag=-1;//存在60以下科目,TCL
            else if(a<85)flag=0;//存在85以下科目,無法成為優秀學生
            else sum+=a;//計算當前總分
        }
        if(sum<90*m&&flag==1)flag=0;//平均分小於90,無法成為優秀學生
        if(flag==-1)printf("No.%d TCL\n",i);
        else if(flag==1)printf("No.%d TQL\n",i);
    }

市質檢的分數 [前綴和]

題意:

求區間[a,b]內的平均分

題解:

這道題是經典的前綴和題目,所以首先介紹一下前綴和思想:

  • 經典oj題目:《校門外的樹-困難
  • 在數組arr[]中用arr[i]記錄arr[0]~arr[i]的和,這一思想使得我們需要區間[a,b]的和時,只需要調用arr[a-1]和arr[b],通過arr[b]-arr[a-1]就可以得到

剩下的直接看程式碼吧

    for (int i = 1; i <= n; i++) {
        int sum=0,num;
        for(int j=1;j<=9;j++) {
            scanf("%d", &num);//分數讀入
            if (i>1)arr[i][j] =arr[i-1][j]+num;//記錄前綴和
            else arr[i][j] = num;
            sum+=num;
        }
        arr[i][0]=sum+arr[i-1][0];//在arr[i][0]記錄綜合前綴和,本題因為數值不大,所以可以使用int
    }

迴文姓名

題意:

一個人的姓名中第一個字和最後一個字的拼音相同,則認為該名字為迴文姓名

題解:

一道字元串比對題目,本題可以簡單分為三類:(1)姓氏的拼音長度大於名(2)姓氏的拼音長度等於名(3)姓氏的拼音長度小於名

  1. 第一類,直接輸出「no」
  2. 第二類,直接判斷是否相等
  3. 第三題,從第二個字元串末位開始,取出和姓氏相等長度的字元串進行比對;同時需要判斷剩餘的字母是否符合拼音標準
    • 關於檢查標準:按照題意,只需要檢查是否包含韻母即可(要檢查合理性的話這題考察的內容就偏了)
    int len1=s1.length(),len2=s2.length();
    if(len1>len2) cout<<"No\n";
    else if(len1==len2){
        if(s1==s2)cout<<"Yes\n";
        else cout<<"No\n";
    }
    else{
        string s3=s2.substr(len2-len1);
        s3[0]= toupper(s3[0]);//令第三個字的首字母大寫,方便同姓氏拼音比對
        s2[0]= tolower(s2[0]);//令第二個字的首字母小寫,方便韻母比對
        bool flag=(s1==s3);
        int flag2=0;
        for(int j=0;j<len2-len1&&flag==1;j++){
            if(s2[j]=='a'||s2[j]=='e'||s2[j]=='i'||s2[j]=='o'||s2[j]=='u'||s2[j]=='v')
                flag2++;
        }
        if(flag&&flag2)cout<<"Yes\n";//flag大於0則說明第二個字中包含韻母,符合題意中的檢查要求
        else cout<<"No\n";
    }

牧夫座空洞 [BFS/DFS]

題意:

三維搜索連續的0的個數,返回最大值

題解:

使用BFS/DFS演算法,主要為演算法的變式,與常規的BFS題目不同的是,本題是求面積範圍而不是給定起點與重點求最短路,所以本題需要自己擬定起點,我給出的程式碼只能說中規中矩,有很多可以優化的地方,感興趣的可以自己進行優化。

  1. 關於起點的擬定,我這裡採用了O(n^3)的複雜度,遍歷整個三維數組,應該會有更好的方法
  2. 當剩餘的「空洞」數量少於當前的max值時,其實不需要繼續搜索
  3. (常規bfs優化):在記錄是否可以經過的「地圖」外側加上一圈的「障礙物」,這樣就不需要進行越界判斷

以上為我認為可以優化的兩個地方。

程式碼:

(1)程式碼一:BFS,使用隊列

關於隊列知識,可以查看我的部落格《[C++STL] 隊列 queue 的入門

#include<bits/stdc++.h>

using namespace std;

struct point {
    int x, y, z;
};

int L, R, C;//記錄三個方向的長度
int g[11][11][11];//記錄是否為空洞
bool dist[11][11][11];//記錄該位置是否被檢索過
int dx[] = {1, -1, 0, 0, 0, 0};//方向數組
int dy[] = {0, 0, 1, -1, 0, 0};//方向數組
int dz[] = {0, 0, 0, 0, 1, -1};//方向數組
queue<point> q;//聲明一個隊列,隊列沒有學過的可以去看我的往期部落格
int bfs(int i, int j, int k) {
    q.push({i,j,k});//起點進入隊列
    int v = 0;//記錄共有多少個連續的元素
    dist[i][j][k] = true;//標記該點已經走過
    while (!q.empty()) {
        point t = q.front();//獲取隊頭數據
        q.pop();//出隊(丟棄)
        v++;
        for (int i = 0; i < 6; i++) {
            int di = t.x + dx[i], dj = t.y + dy[i], dk = t.z + dz[i];
            if (di >= 0 && di < L && dj >= 0 && dj < R && dk >= 0 && dk < C && g[di][dj][dk]==0 && !dist[di][dj][dk]) {
//這一行首先判斷了是否越界,然後判斷該點是否可以通過,然後判斷是否已經過
                dist[di][dj][dk] = true;//標記該點已經走過
                q.push({di, dj, dk});//符合的點,入隊
            }
        }
    }
    return v;
}

int main() {
    int t;
    cin >> t;
    while (t--) {
        cin>>L>>R>>C;
        memset(dist, 0, sizeof dist);
        for (int i = 0; i < L; i++)
            for (int j = 0; j < R; j++)
                for (int k = 0; k < C; k++)
                    cin>>g[i][j][k];//輸入三維數組
        int ma = 0;
        for (int i = 0; i < L; i++)
            for (int j = 0; j < R; j++)
                for (int k = 0; k < C; k++)
                    if (!dist[i][j][k]&&g[i][j][k]==0)//如果當前的位置未被檢索過,且為「空洞」
                        ma = max(ma, bfs(i,j,k));//將改點作為起點進行bfs搜索
        cout << ma << endl;
    }
    return 0;
}

View Code

(2)程式碼二:BFS,使用遞歸

int bfs(int i, int j, int k) {
    if (i < 0 || i >= L || j < 0 || j >= R || k < 0 || k >= C)return 0;//判斷越界
    if (dist[i][j][k] || g[i][j][k] == 1)return 0;//已經走過或者不為「空洞」
    dist[i][j][k] = true;//標記已經走過
    return 1 + bfs(i, j, k + 1) + bfs(i, j, k - 1),bfs(i, j + 1, k), bfs(i, j - 1, k) +bfs(i + 1, j, k), bfs(i - 1, j, k);//向六個方向試探
}

孤獨的島嶼 [並查集]

題意:

島嶼和島嶼之間通過橋樑進行連接,有的島嶼之間有橋樑,有的島嶼之間沒有橋樑,但可以經由通往別的島嶼的橋樑繞行到目的島嶼。如果完全無法通行,只能租用水上飛機才能到達該島嶼。以第1座島嶼為起點,想要遊歷一遍所有的島嶼,並且最終回到第1座島嶼,問他至少需要租用多少次水上飛機?

題解:

  1. 建立並查集,關於並查集,可以查看我的部落格《[並查集] 問題列表#1192:朋友
  2. 判斷find(i)的值的個數
    • 這裡給出兩種思路吧
      • (1)存入set容器,輸出set.size()
            set<int>ans;
            for(int j=1;j<=m;j++)
                ans.insert(k[find(j)]);
            if(ans.size()==1)cout<<0<<endl;//由於最終需要回到起始島嶼,若全部聯通則不需要
            else cout<<ans.size()<<endl;
      • (2)建立一個bool數組 b[],將b[find(i)]=1,再遍歷一次bool數組
            for(int j=1;j<=m;j++)
                k[find(j)]=1;
            int ans=0;
            for(int j=1;j<=m;j++)
                if(k[j])ans++;
            if(ans==1)ans=0;
            cout<<ans<<endl;

 第五題使用矩陣快速冪,只能說再等等,可能等不到了(課設太多了啊喂!)

 

 

 

製作:BDT20040

Tags: