2017-第八屆藍橋杯大賽個人賽省賽(軟件類)真題 C大學A組


返回目錄

題目一覽:

1.迷宮

2.跳蚱蜢

3.魔方狀態

4.方格分割

5.字母組串

6.最大公共子串

7.正則問題

8.包子湊數

9.分巧克力

10.油漆面積

1.迷宮

X星球的一處迷宮游樂場建在某個小山坡上。
它是由10x10相互連通的小房間組成的。

房間的地板上寫着一個很大的字母。
我們假設玩家是面朝上坡的方向站立,則:
L表示走到左邊的房間,
R表示走到右邊的房間,
U表示走到上坡方向的房間,
D表示走到下坡方向的房間。

X星球的居民有點懶,不願意費力思考。
他們更喜歡玩運氣類的游戲。這個游戲也是如此!

開始的時候,直升機把100名玩家放入一個個小房間內。
玩家一定要按照地上的字母移動。

迷宮地圖如下:
------------
UDDLUULRUL
UURLLLRRRU
RRUURLDLRD
RUDDDDUUUU
URUDLLRRUU
DURLRLDLRL
ULLURLLRDU
RDLULLRDDD
UUDDUDUDLL
ULRDLUURRR
------------

請你計算一下,最后,有多少玩家會走出迷宮?
而不是在里邊兜圈子。

請提交該整數,表示走出迷宮的玩家數目,不要填寫任何多余的內容。

如果你還沒明白游戲規則,可以參看一個簡化的4x4迷宮的解說圖:
p1.png

思路:模擬即可。走出判定:出界;走不出判定:走到了原來走過的點。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 string s[101];
 5 bool flag, vis[11][11], Break;
 6 int Ans;
 7 
 8 void dfs(int x, int y) {
 9     if(x<0 || x>9 || y<0 || y>9) { // 走出 
10         flag = true;
11         return ;
12     }
13     if(vis[x][y]) { // 走到了走過的點 
14         Break = true;
15         return ;
16     }
17     if(Break || flag) return;
18     vis[x][y] = true; // 標記 
19     if(s[x][y] == 'U') x--; // 下一步 
20     else if(s[x][y] == 'D') x++;
21     else if(s[x][y] == 'L') y--;
22     else if(s[x][y] == 'R') y++;
23     dfs(x, y);
24 }
25 
26 int main() {
27     for(int i=0; i<10; ++i) 
28         cin >> s[i];
29     for(int i=0; i<10; ++i) {
30         for(int j=0; j<10; ++j) {
31             flag = Break = false;// 每次都要初始化 
32             memset(vis, false, sizeof(vis)); 
33             dfs(i, j);
34             if(flag) Ans++;
35         }
36     }
37     printf("%d\n", Ans);
38     return 0;
39 }
1.迷宮

答案:31

 

2.跳蚱蜢

如圖 p1.png 所示:

有9只盤子,排成1個圓圈。
其中8只盤子內裝着8只蚱蜢,有一個是空盤。
我們把這些蚱蜢順時針編號為 1~8

每只蚱蜢都可以跳到相鄰的空盤中,
也可以再用點力,越過一個相鄰的蚱蜢跳到空盤中。

請你計算一下,如果要使得蚱蜢們的隊形改為按照逆時針排列,
並且保持空盤的位置不變(也就是1-8換位,2-7換位,...),至少要經過多少次跳躍?

注意:要求提交的是一個整數,請不要填寫任何多余內容或說明文

思路:寬搜,每次將四種跳法后的局面判重加入即可。

 1 #include <bits/stdc++.h>
 2 #include <queue> 
 3 using namespace std;
 4 
 5 char ss[1010][1010];
 6 int len;
 7 set<string> vis;
 8 
 9 struct Node {
10     string s; // 當前局面 
11     int pos, cnt; // 空格位置  到達該局面的步數 
12 };
13 
14 void bfs() {
15     queue<Node> q;
16     Node head;
17     head.pos = head.cnt = 0; head.s = "012345678"; // 初始局面 
18     q.push(head);
19     while(!q.empty()) {
20         head = q.front(); q.pop();
21         if(head.s == "087654321") { // 到達目標局面 
22             printf("Ans = %d\n", head.cnt);
23             return ;
24         }
25         for(int i=-2; i<=2; ++i) { // 四種跳法 
26             if(i == 0) continue;
27             string str = head.s;
28             int tmp = head.pos;
29             swap(str[tmp], str[(tmp+i+9)%9]);
30             Node tail;
31             if(vis.count(str) == 0) { // 當前局面之前沒有 
32                 vis.insert(str);
33                 tail.s = str;
34                 tail.pos = (tmp+i+9)%9;
35                 tail.cnt = head.cnt+1;
36                 q.push(tail);
37             }
38         }
39     }
40 }
41 
42 int main() {
43     bfs();
44     return 0;
45 }
2.跳蚱蜢

答案:20

 

3.魔方狀態

二階魔方就是只有2層的魔方,只由8個小塊組成。
如圖p1.png所示。

小明很淘氣,他只喜歡3種顏色,所有把家里的二階魔方重新塗了顏色,如下:

前面:橙色
右面:綠色
上面:黃色
左面:綠色
下面:橙色
后面:黃色

請你計算一下,這樣的魔方被打亂后,一共有多少種不同的狀態。

如果兩個狀態經過魔方的整體旋轉后,各個面的顏色都一致,則認為是同一狀態。

請提交表示狀態數的整數,不要填寫任何多余內容或說明文字。

答案:229878

 

4.方格分割

6x6的方格,沿着格子的邊線剪開成兩部分。
要求這兩部分的形狀完全相同。

如圖:p1.png, p2.png, p3.png 就是可行的分割法。

試計算:
包括這3種分法在內,一共有多少種不同的分割方法。
注意:旋轉對稱的屬於同一種分割法。

請提交該整數,不要填寫任何多余的內容或說明文字。

思路:這道題很巧妙,不按照格子搜索,搜索點,從中心點開始深搜,同時標記對稱的點,當一邊搜完時就是一個可行的方案。最后記得/4.

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 int Ans;
 5 int u[4] = {-1, 0, 0, 1}, v[4] = {0, -1, 1, 0};
 6 bool vis[10][10];
 7 
 8 bool check(int x, int y) {
 9     if(vis[x][y]) return false; // 走過了 
10     if(x<0 || x>6) return false; // 出界 
11     if(y<0 || y>6) return false;
12     return true;
13 }
14 
15 void dfs(int x, int y) {
16     if(x==0 || x==6 || y==0 || y==6) { // 搜到邊界了 
17         Ans ++;
18         return ;
19     }
20     vis[x][y] = vis[6-x][6-y] = true; // 標記
21     for(int i=0; i<4; ++i) { // 四個方向 
22         int xx = x + u[i];
23         int yy = y + v[i];
24         if(check(xx, yy)) {
25             dfs(xx, yy);
26         }
27     }
28     vis[x][y] = vis[6-x][6-y] = false; // 回溯 
29 }
30 
31 int main() {
32     memset(vis, false, sizeof(vis));
33     dfs(3, 3); // 從中心點開始搜索 
34     printf("%d\n", Ans/4); // 有重復 
35     return 0;
36 }
4.方格分割

答案:509

 

5.字母組串

由 A,B,C 這3個字母就可以組成許多串。
比如:"A","AB","ABC","ABA","AACBB" ....

現在,小明正在思考一個問題:
如果每個字母的個數有限定,能組成多少個已知長度的串呢?

他請好朋友來幫忙,很快得到了代碼,
解決方案超級簡單,然而最重要的部分卻語焉不詳。

請仔細分析源碼,填寫划線部分缺少的內容。

 1 #include <stdio.h>
 2 
 3 // a個A,b個B,c個C 字母,能組成多少個不同的長度為n的串。
 4 int f(int a, int b, int c, int n)
 5 {
 6     if(a<0 || b<0 || c<0) return 0;
 7     if(n==0) return 1; 
 8     
 9     return _________________;  // 填空
10 }
11 
12 int main()
13 {
14     printf("%d\n", f(1,1,1,2));
15     printf("%d\n", f(1,2,3,3));
16     return 0;
17 }

對於上面的測試數據,小明口算的結果應該是:
6
19

注意:只填寫划線部分缺少的代碼,不要提交任何多余內容或說明性文字。

思路:遞歸題,通過第7行知道n是目前需要填充的長度,那么我們每次填充一個A、B或者C,那么其個數需要減1,目前需要填充的長度也要減1.

答案:

f(a-1, b, c, n-1)+f(a, b-1, c, n-1)+f(a, b, c-1, n-1)

 

6.最大公共子串

最大公共子串長度問題就是:
求兩個串的所有子串中能夠匹配上的最大長度是多少。

比如:"abcdkkk" 和 "baabcdadabc",
可以找到的最長的公共子串是"abcd",所以最大公共子串長度為4。

下面的程序是采用矩陣法進行求解的,這對串的規模不大的情況還是比較有效的解法。

請分析該解法的思路,並補全划線部分缺失的代碼。

 

 1 #include <stdio.h>
 2 #include <string.h>
 3 
 4 #define N 256
 5 int f(const char* s1, const char* s2)
 6 {
 7     int a[N][N];
 8     int len1 = strlen(s1);
 9     int len2 = strlen(s2);
10     int i,j;
11     
12     memset(a,0,sizeof(int)*N*N);
13     int max = 0;
14     for(i=1; i<=len1; i++){
15         for(j=1; j<=len2; j++){
16             if(s1[i-1]==s2[j-1]) {
17                 a[i][j] = ______;  //填空
18                 if(a[i][j] > max) max = a[i][j];
19             }
20         }
21     }
22     
23     return max;
24 }
25 
26 int main()
27 {
28     printf("%d\n", f("abcdkkk", "baabcdadabc"));
29     return 0;
30 }

注意:只提交缺少的代碼,不要提交已有的代碼和符號。也不要提交說明性文字。

思路:DP題,看第16行,當前兩個字符相同,那么以他倆結尾的長度就是以前一個字符結尾的長度+1。針對最長公共子序列的問題可以看着。(待補)

答案:

a[i-1][j-1] + 1

 

7.正則問題

考慮一種簡單的正則表達式:
只由 x ( ) | 組成的正則表達式。
小明想求出這個正則表達式能接受的最長字符串的長度。

例如 ((xx|xxx)x|(x|xx))xx 能接受的最長字符串是: xxxxxx,長度是6。

輸入
----
一個由x()|組成的正則表達式。輸入長度不超過100,保證合法。

輸出
----
這個正則表達式能接受的最長字符串的長度。

例如,
輸入:
((xx|xxx)x|(x|xx))xx

程序應該輸出:
6

資源約定:
峰值內存消耗(含虛擬機) < 256M
CPU消耗 < 1000ms


請嚴格按要求輸出,不要畫蛇添足地打印類似:“請您輸入...” 的多余內容。

注意:
main函數需要返回0;
只使用ANSI C/ANSI C++ 標准;
不要調用依賴於編譯環境或操作系統的特殊函數。
所有依賴的函數必須明確地在源文件中 #include <xxx>
不能通過工程設置而省略常用頭文件。

提交程序時,注意選擇所期望的語言類型和編譯器類型。

思路:首先理解樣例的6是這么來的,|是取左右里較多的哪一個。((xx|xxx)x|(x|xx))xx,

第一步去括號里面左側的括號:(xxx x|(x|xx))xx

第二步去括號里面右側的括號:(xxx x| xx)xx

第三步去外層括號:xxx x xx

答案就是6.

代碼的話就是遞歸,然后是四種情況。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 char s[105];
 5 int pos = 0, len;
 6 
 7 int f() {
 8     int Now_sum = 0, Max_sum = 0;
 9     while(pos < len) {
10         if(s[pos] == '(') { // 左括號,位置加一 等待右括號 
11             pos++;
12             Now_sum += f();
13         } 
14         else if(s[pos] == 'x') { // x 位置加一 計數器加一 
15             pos ++;
16             Now_sum ++;
17         }
18         else if(s[pos] == ')') { // 左括號 位置加一 中斷這次遞歸 
19             pos ++;
20             break;
21         }
22         else if(s[pos] == '|') { // | 位置加一  
23             pos++;
24             Max_sum = max(Max_sum, Now_sum); // 保留較大值 
25             Now_sum = 0; //計數器清零 
26         }
27     }
28     Max_sum = max(Max_sum, Now_sum); //保留較大值 
29     return Max_sum;
30 }
31 
32 int main() {
33     scanf("%s", s);
34     len = strlen(s);
35     int Ans = f();
36     printf("%d\n", Ans);
37     return 0;
38 }
7.正則問題

 

8.包子湊數

小明幾乎每天早晨都會在一家包子鋪吃早餐。他發現這家包子鋪有N種蒸籠,其中第i種蒸籠恰好能放Ai個包子。每種蒸籠都有非常多籠,可以認為是無限籠。

每當有顧客想買X個包子,賣包子的大叔就會迅速選出若干籠包子來,使得這若干籠中恰好一共有X個包子。比如一共有3種蒸籠,分別能放3、4和5個包子。當顧客想買11個包子時,大叔就會選2籠3個的再加1籠5個的(也可能選出1籠3個的再加2籠4個的)。

當然有時包子大叔無論如何也湊不出顧客想買的數量。比如一共有3種蒸籠,分別能放4、5和6個包子。而顧客想買7個包子時,大叔就湊不出來了。

小明想知道一共有多少種數目是包子大叔湊不出來的。

輸入
----
第一行包含一個整數N。(1 <= N <= 100)
以下N行每行包含一個整數Ai。(1 <= Ai <= 100)

輸出
----
一個整數代表答案。如果湊不出的數目有無限多個,輸出INF。

例如,
輸入:
2
4
5

程序應該輸出:
6

再例如,
輸入:
2
4
6

程序應該輸出:
INF

樣例解釋:
對於樣例1,湊不出的數目包括:1, 2, 3, 6, 7, 11。
對於樣例2,所有奇數都湊不出來,所以有無限多個。

資源約定:
峰值內存消耗(含虛擬機) < 256M
CPU消耗 < 1000ms


請嚴格按要求輸出,不要畫蛇添足地打印類似:“請您輸入...” 的多余內容。

注意:
main函數需要返回0;
只使用ANSI C/ANSI C++ 標准;
不要調用依賴於編譯環境或操作系統的特殊函數。
所有依賴的函數必須明確地在源文件中 #include <xxx>
不能通過工程設置而省略常用頭文件。

提交程序時,注意選擇所期望的語言類型和編譯器類型。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 int n, a[1010], Ans, g;
 5 bool f[10010];
 6 
 7 int gcd(int a, int b) {
 8     return b? gcd(b, a%b):a;
 9 }
10 
11 int main() {
12     cin >> n;
13     memset(f, false, sizeof(f));
14     f[0] = true;
15     for(int i=0; i<n; ++i) {
16         scanf("%d", &a[i]);
17         if(i == 0) g = a[i];
18         else g = gcd(g, a[i]);
19         for(int j=0; j<10000; ++j)
20             if(f[j]) f[j+a[i]] = true;
21     }
22     if(g != 1) {
23         puts("INF");
24         return 0;
25     }
26     bool flag = false;
27     int tot = 0;
28     for(int i=0; i<10000; ++i) {
29         if(!f[i]) Ans++;
30     }
31     printf("%d\n", Ans);
32     return 0;
33 }
8.包子湊數

 

9.分巧克力

兒童節那天有K位小朋友到小明家做客。小明拿出了珍藏的巧克力招待小朋友們。
小明一共有N塊巧克力,其中第i塊是Hi x Wi的方格組成的長方形。

為了公平起見,小明需要從這 N 塊巧克力中切出K塊巧克力分給小朋友們。切出的巧克力需要滿足:

1. 形狀是正方形,邊長是整數
2. 大小相同

例如一塊6x5的巧克力可以切出6塊2x2的巧克力或者2塊3x3的巧克力。

當然小朋友們都希望得到的巧克力盡可能大,你能幫小Hi計算出最大的邊長是多少么?

輸入
第一行包含兩個整數N和K。(1 <= N, K <= 100000)
以下N行每行包含兩個整數Hi和Wi。(1 <= Hi, Wi <= 100000)
輸入保證每位小朋友至少能獲得一塊1x1的巧克力。

輸出
輸出切出的正方形巧克力最大可能的邊長。

樣例輸入:
2 10
6 5
5 6

樣例輸出:
2

資源約定:
峰值內存消耗(含虛擬機) < 256M
CPU消耗 < 1000ms


請嚴格按要求輸出,不要畫蛇添足地打印類似:“請您輸入...” 的多余內容。

注意:
main函數需要返回0;
只使用ANSI C/ANSI C++ 標准;
不要調用依賴於編譯環境或操作系統的特殊函數。
所有依賴的函數必須明確地在源文件中 #include <xxx>
不能通過工程設置而省略常用頭文件。

提交程序時,注意選擇所期望的語言類型和編譯器類型。

方法一:要求邊長最大那么我們就從最大開始枚舉邊長,不斷減小來找到滿足條件的邊長。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 int N, K;
 5 
 6 struct QKL {
 7     int h, w;
 8 }qkl[100010];
 9 
10 int main() { // 暴力 
11     cin >> N >> K;
12     for(int i=0; i<N; ++i)
13         scanf("%d%d", &qkl[i].h, &qkl[i].w);
14     int Ans = 100000;
15     while(Ans >= 1) { // 枚舉邊長 
16         int cnt = 0; // 當前邊長下能切多少 
17         for(int i=0; i<N; ++i) // N塊巧克力 
18             cnt += (qkl[i].h/Ans) * (qkl[i].w/Ans);
19         if(cnt >= K) { // 由於我們從大到小枚舉,第一個滿足的就是最大的 
20             printf("%d\n", Ans);
21             return 0;
22         }
23         Ans --;
24     }
25     return 0;
26 }
9.分巧克力-方法一-超時

方法二:優化方法一,我們使用二分法,每次二分出一個邊長,不夠分說明邊長大了,我們就減小一點。分的多,我們看看能不能使邊長大一點。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 int N, K;
 5 
 6 struct QKL {
 7     int h, w;
 8 }qkl[100010];
 9 
10 bool check(int x) {
11     int cnt = 0;
12     for(int i=0; i<N; ++i) 
13         cnt += (qkl[i].h/x) * (qkl[i].w/x);
14     if(cnt >= K) return true; // 能分成K塊 
15     else return false; // 分不成K塊 
16 }
17 
18 int main() { // 二分 
19     cin >> N >> K;
20     for(int i=0; i<N; ++i)
21         scanf("%d%d", &qkl[i].h, &qkl[i].w);
22     int l = 0, r = 100001;
23     while(l <= r) {
24         int m = (l+r) / 2;
25         if(check(m)) l = m+1; // 邊長可以再大一點 
26         else r = m-1; // 邊長大了,小一點 
27     }
28     printf("%d\n", l-1);
29     return 0;
30 } 
9.分巧克力-方法二

 

10.油漆面積

X星球的一批考古機器人正在一片廢墟上考古。
該區域的地面堅硬如石、平整如鏡。
管理人員為方便,建立了標准的直角坐標系。

每個機器人都各有特長、身懷絕技。它們感興趣的內容也不相同。
經過各種測量,每個機器人都會報告一個或多個矩形區域,作為優先考古的區域。

矩形的表示格式為(x1,y1,x2,y2),代表矩形的兩個對角點坐標。

為了醒目,總部要求對所有機器人選中的矩形區域塗黃色油漆。
小明並不需要當油漆工,只是他需要計算一下,一共要耗費多少油漆。

其實這也不難,只要算出所有矩形覆蓋的區域一共有多大面積就可以了。
注意,各個矩形間可能重疊。

本題的輸入為若干矩形,要求輸出其覆蓋的總面積。

輸入格式:
第一行,一個整數n,表示有多少個矩形(1<=n<10000)
接下來的n行,每行有4個整數x1 y1 x2 y2,空格分開,表示矩形的兩個對角頂點坐標。
(0<= x1,y1,x2,y2 <=10000)

輸出格式:
一行一個整數,表示矩形覆蓋的總面積。

例如,
輸入:
3
1 5 10 10
3 1 20 20
2 7 15 17

程序應該輸出:
340

再例如,
輸入:
3
5 2 10 6
2 7 12 10
8 1 15 15

程序應該輸出:
128

資源約定:
峰值內存消耗(含虛擬機) < 256M
CPU消耗 < 2000ms


請嚴格按要求輸出,不要畫蛇添足地打印類似:“請您輸入...” 的多余內容。

注意:
main函數需要返回0;
只使用ANSI C/ANSI C++ 標准;
不要調用依賴於編譯環境或操作系統的特殊函數。
所有依賴的函數必須明確地在源文件中 #include <xxx>
不能通過工程設置而省略常用頭文件。

提交程序時,注意選擇所期望的語言類型和編譯器類型。

方法一:直接模擬,設置一個二維數組初始化為false,需要塗油漆的地方改為true,最后統計true的個數即可。由於官方數據較水,該方法不會超時。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 int N, Ans = 0;
 5 bool vis[10006][10006];
 6 
 7 void work(int x, int y, int xx, int yy) {
 8     for(int i=x; i<xx; ++i) 
 9         for(int j=y; j<yy; ++j)
10             vis[i][j] = true;
11 }
12 
13 int main() {
14     cin >> N;
15     memset(vis, false, sizeof(vis));
16     for(int i=1; i<=N; ++i) {
17         int x, y, xx, yy;
18         scanf("%d%d%d%d", &x, &y, &xx, &yy);
19         work(x, y, xx, yy);
20     }
21     for(int i=0; i<10005; ++i) 
22         for(int j=0; j<10005; ++j)
23             if(vis[i][j]) {
24                 //printf("%d %d\n", i, j);
25                 Ans++;
26             }
27     printf("%d\n", Ans);
28     return 0;
29 }
10.油漆面積-方法一

方法二:正解解法:線段樹+掃描線。

先挖個坑

PS:官方有個數據是錯的,正確答案是4909,而給的答案是3796.想AC的話可以特判一下。面向數據編程

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM