2013年藍橋杯省賽C/C++A組真題解析


1、高斯日記

大數學家高斯有個好習慣:無論如何都要記日記。

他的日記有個與眾不同的地方,他從不注明年月日,而是用一個整數代替,比如:4210

后來人們知道,那個整數就是日期,它表示那一天是高斯出生后的第幾天。這或許也是個好習慣,它時時刻刻提醒着主人:日子又過去一天,還有多少時光可以用於浪費呢?

高斯出生於:1777年4月30日。

在高斯發現的一個重要定理的日記上標注着:5343,因此可算出那天是:1791年12月15日。

高斯獲得博士學位的那天日記上標着:8113

請你算出高斯獲得博士學位的年月日。

提交答案的格式是:yyyy-mm-dd, 例如:1980-03-21

 

 此題的易錯點在於是否要算上高斯出生的那天,以及輸出格式。

 

C++解法:

 1 //從出生年開始算起,這種題一般都要掐頭去尾,我直接把頭加上了,
 2 //出生第一天也算是活了一天,所以加119就行,
 3 
 4 #include<iostream>
 5 using namespace std;
 6 int month[13] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
 7 int main()
 8 {
 9     int n;
10     while (cin >> n)
11     {
12         n += 119;
13         int year = 1777;
14         while (n >= 365)//算出n所在的年份
15         {
16             if ((year % 4 == 0 && year % 400 != 0) || year % 400 == 0)
17                 n -= 366;
18             else
19                 n -= 365;
20             year++;
21         }
22         int mon;
23         if ((year % 4 == 0 && year % 400 != 0) || year % 400 == 0)
24             month[1]++;//如果是閏年2月天數加一
25         for (mon = 0; mon <= 11; mon++)
26         {
27             if (n > month[mon])
28                 n -= month[mon];//因為可以有4.30,所以這里是>不是>=
29             else
30                 break;
31         }
32 
33         printf("%d-%02d-%02d", year, mon+1, n);     //注意輸出格式
34     }
35     return 0;
36 }

 

 Java解法:

 

 1 import java.util.Calendar;
 2 
 3 public class Demo {
 4 
 5     public static void main(String[] args) {
 6         Calendar c = Calendar.getInstance();
 7         //注意月份是從0開始算的,所以要設置為3月30日
 8         c.set(1777, 3, 30);
 9         c.add(Calendar.DAY_OF_MONTH, 8112);
10 
11         //注意月份是從0開始算的,所以結果中的月份要+1
12         System.out.printf("%d-%02d-%02d\n", c.get(Calendar.YEAR),
13                 c.get(Calendar.MONTH) + 1, c.get(Calendar.DAY_OF_MONTH));
14     }
15 
16 }

 

 

 

 

 

    因為1777年不在1999-9999年的可計算范圍之內,所以可以退后兩千年進行計算,此時需將結果減去2000年。

 答案:1799-07-16

2、排他平方數

小明正看着 203879 這個數字發呆。

原來,203879 * 203879 = 41566646641

這有什么神奇呢?仔細觀察,203879 是個6位數,並且它的每個數位上的數字都是不同的,並且它平方后的所有數位上都不出現組成它自身的數字。

具有這樣特點的6位數還有一個,請你找出它!

再歸納一下篩選要求:
1. 6位正整數
2. 每個數位上的數字不同
3. 其平方數的每個數位不含原數字的任何組成數位

答案是一個6位的正整數。

 

解法一: 未優化,完全蠻力

 1 #include<iostream>  
 2 #include<sstream>
 3 
 4 using namespace std;
 5 
 6 int main()
 7 {
 8     for (long long i = 100000; i <= 999999; ++i)
 9     {
10         long long a = i * i;
11         int yi = i % 10;  //個位
12         int er = (i % 100) / 10;    //十位
13         if (er == yi)
14             continue;
15         int san = (i % 1000) / 100; //百位
16         if (san == yi || san == er)
17             continue;
18         int si = (i % 10000) / 1000;    //千位
19         if (si == san || si == er || si == yi)
20             continue;
21         int wu = (i % 100000) / 10000;    //萬位
22         if (wu == si || wu == san || wu == er || wu == yi)
23             continue;
24         int liu = i / 100000;    //十萬位
25         if (liu == wu || liu == si || liu == san || liu == er || liu == yi)
26             continue;
27         char c1, c2, c3, c4, c5, c6;
28         c1 = char(yi + '0');
29         c2 = char(er + '0');
30         c3 = char(san + '0');
31         c4 = char(si + '0');
32         c5 = char(wu + '0');
33         c6 = char(liu + '0');
34 
35         //int轉為string
36         stringstream ss;
37         ss << a;
38         string s = ss.str();
39 
40         int flag = 0;
41         for (int j = 0; j < s.size(); ++j)
42         {
43             if (s[j] == c1 || s[j] == c2 || s[j] == c3 || s[j] == c4 || s[j] == c5 || s[j] == c6)
44             {
45                 flag = 1;
46                 break;
47             }
48         }
49         if(flag == 0)
50             cout << i << ": " << a << endl;
51     }
52 
53 
54     return 0;
55 }

 

解法二:(推薦掌握)

 1 #include<iostream>
 2 #include<cstring>
 3 using namespace std;
 4 
 5 int vis[10];  //使用vis[j]保存i中數字j出現的次數
 6 
 7 bool judge(long long int i)    //判斷i中是否有重復的數字
 8 {
 9     while (i)
10     {
11         if (vis[i % 10] == 0) //取i的最后一位數字,並保存其出現次數
12             vis[i % 10]++;
13         else                 //如果該數字已經出現過
14             return false;
15         i /= 10;    //判斷下一個數字
16     }
17     return true;
18 }
19 bool judge1(long long int i)    //判斷結果中是否包含i中的數字
20 {
21     while (i)
22     {
23         if (vis[i % 10] != 0)    //該數字已經在i中出現過
24             return false;        //返回錯誤
25         i /= 10;        //判斷下一個數字
26     }
27     return true;
28 }
29 int main()
30 {
31     long long int i;
32     for (i = 123456; i <= 987654; i++)    //優化枚舉范圍
33     {
34         memset(vis, 0, sizeof(vis));
35         if (!judge(i))        //判斷i中是否有重復數字
36             continue;    
37         if (!judge1(i*i))    //判斷i的平方中是否包含i中的數字
38             continue;    
39         cout << i << endl;
40     }
41     return 0;
42 }

 答案:639172

3、振興中華

小明參加了學校的趣味運動會,其中的一個項目是:跳格子。

地上畫着一些格子,每個格子里寫一個字,如下所示:(也可參見p1.jpg)

從我做起振
我做起振興
做起振興中
起振興中華


比賽時,先站在左上角的寫着“從”字的格子里,可以橫向或縱向跳到相鄰的格子里,但不能跳到對角的格子或其它位置。一直要跳到“華”字結束。


要求跳過的路線剛好構成“從我做起振興中華”這句話。

請你幫助小明算一算他一共有多少種可能的跳躍路線呢?

 

注意:使用char數組保存字符串結果時,最后一定要補上'\0',不然會產生錯誤結果!

 1 #include<iostream>
 2 #include<cstring>
 3 
 4 using namespace std;
 5 
 6 int vis[7][7];    //記錄訪問狀態
 7 int dx[4] = { 1,-1,0,0 };
 8 int dy[4] = { 0,0,1,-1 };
 9 char s[30];        //用字符數組保存路徑上的值,一定要記住最后補上'\0'
10 int result = 0;    //保存總路線數
11 
12 char mp[7][7] = {
13     "ABCDE",
14     "BCDEF",
15     "CDEFG",
16     "DEFGH"
17 };
18 
19 void dfs(int x, int y, int step)
20 {
21     s[step] = mp[x][y];  //用字符數組保存路徑上的值
22 
23     if (mp[x][y] == 'H')
24     {
25         s[step + 1] = '\0';        //一定要補上'\0',不然會產生錯誤結果
26         if (strcmp(s, "ABCDEFGH") == 0)
27             ++result;
28         return;
29     }
30 
31     for (int i = 0; i <= 3; ++i)
32     {
33         int xx = x + dx[i];
34         int yy = y + dy[i];
35 
36         if (xx < 0 || yy < 0 || xx >= 5 || yy >= 5)
37             continue;
38 
39         if (!vis[xx][yy])
40         {
41             vis[xx][yy] = 1;
42             dfs(xx, yy, step + 1);
43             vis[xx][yy] = 0;    //回溯后一定要重新置為未訪問狀態
44         }
45 
46     }
47 }
48 
49 int main()
50 {
51     memset(vis, 0, sizeof(vis));
52     vis[0][0] = 1;
53     dfs(0,0,0);
54     cout << result << endl;
55     return 0;
56 }

 答案:35

4、顛倒的價牌

小李的店里專賣其它店中下架的樣品電視機,可稱為:樣品電視專賣店。

其標價都是4位數字(即千元不等)。

小李為了標價清晰、方便,使用了預制的類似數碼管的標價簽,只要用顏色筆塗數字就可以了(參見p1.jpg)。

這種價牌有個特點,對一些數字,倒過來看也是合理的數字。如:1 2 5 6 8 9 0 都可以。這樣一來,如果牌子掛倒了,有可能完全變成了另一個價格,比如:1958 倒着掛就是:8561,差了幾千元啊!!

當然,多數情況不能倒讀,比如,1110 就不能倒過來,因為0不能作為開始數字。

有一天,悲劇終於發生了。某個店員不小心把店里的某兩個價格牌給掛倒了。並且這兩個價格牌的電視機都賣出去了!

慶幸的是價格出入不大,其中一個價牌賠了2百多,另一個價牌卻賺了8百多,綜合起來,反而多賺了558元。

請根據這些信息計算:賠錢的那個價牌正確的價格應該是多少?


答案是一個4位的整數,請通過瀏覽器直接提交該數字。

 

思路:使用數組保存對應下標顛倒后的數字,將原價的每一位數字截取出來,通過數組轉換為顛倒后的價格。具體請參考如下代碼:

 1 #include<iostream>
 2 #include<map>
 3 #include<cstring>
 4 
 5 using namespace std;
 6 
 7 int a[10];
 8 
 9 int main()
10 {
11     a[0] = 0;  a[1] = 1;  a[2] = 2;  a[3] = -1;  a[4] = -1;  a[5] = 5;
12     a[6] = 9;  a[7] = -1; a[8] = 8;  a[9] = 6;
13     map<int, int>mp1;    //map<原價,差價>
14     map<int, int>mp2;
15 
16     for (int i = 1001; i < 9999; ++i)
17     {
18         int ge = i % 10;    //個位
19         int shi = (i / 10) % 10;    //十位
20         int bai = (i / 100) % 10;    //百位
21         int qian = (i / 1000) % 10; //千位
22 
23         if (a[ge] == -1 || a[shi] == -1 || a[bai] == -1 || a[qian] == -1)
24             continue;
25 
26         int inverse = a[ge] * 1000 + a[shi] * 100 + a[bai] * 10 + a[qian];    //顛倒后的價格
27         int cha = inverse - i;    //差價
28         if ((cha > -300 && cha <= -200))    //把虧損200-300元的商品存入map1
29             mp1.insert({ i,cha });            
30         if((cha >= 800 && cha < 900))        //把賺了800-900元的商品存入map2
31             mp2.insert({ i,cha });
32     }
33 
34     for (auto it1 = mp1.begin(); it1 != mp1.end(); it1++)
35     {
36         for (auto it2 = mp2.begin(); it2 != mp2.end(); it2++)
37         {
38             if (it1->second + it2->second == 558)
39             {
40                 cout << it1->first << endl << it2->first << endl;
41                 return 0;
42             }
43         }
44     }
45 
46     return 0;
47 }

 答案:9088

5、前綴判斷

如下的代碼判斷 needle_start指向的串是否為haystack_start指向的串的前綴,如不是,則返回NULL。

比如:"abcd1234" 就包含了 "abc" 為前綴

char* prefix(char* haystack_start, char* needle_start)
{
    char* haystack = haystack_start;
    char* needle = needle_start;

    
    while(*haystack && *needle){
        if(______________________________) return NULL;  //填空位置
    }
    
    if(*needle) return NULL;
    
    return haystack_start;
}

請分析代碼邏輯,並推測划線處的代碼,通過網頁提交。
注意:僅把缺少的代碼作為答案,千萬不要填寫多余的代碼、符號或說明文字!!

 

答案: *haystack++ != *needle++

 

6、逆波蘭表達式

正常的表達式稱為中綴表達式,運算符在中間,主要是給人閱讀的,機器求解並不方便。

例如:3 + 5 * (2 + 6) - 1

而且,常常需要用括號來改變運算次序。

相反,如果使用逆波蘭表達式(前綴表達式)表示,上面的算式則表示為:

- + 3 * 5 + 2 6 1

不再需要括號,機器可以用遞歸的方法很方便地求解。

為了簡便,我們假設:

1. 只有 + - * 三種運算符
2. 每個運算數都是一個小於10的非負整數

下面的程序對一個逆波蘭表示串進行求值。
其返回值為一個結構:其中第一元素表示求值結果,第二個元素表示它已解析的字符數。

struct EV
{
    int result;  //計算結果 
    int n;       //消耗掉的字符數 
};

struct EV evaluate(char* x)
{
    struct EV ev = {0,0};
    struct EV v1;
    struct EV v2;

    if(*x==0) return ev;
    
    if(x[0]>='0' && x[0]<='9'){
        ev.result = x[0]-'0';
        ev.n = 1;
        return ev;
    }
    
    v1 = evaluate(x+1);
    v2 = _____________________________;  //填空位置
    
    if(x[0]=='+') ev.result = v1.result + v2.result;
    if(x[0]=='*') ev.result = v1.result * v2.result;
    if(x[0]=='-') ev.result = v1.result - v2.result;
    ev.n = 1+v1.n+v2.n;

    return ev;
}


請分析代碼邏輯,並推測划線處的代碼,通過網頁提交。
注意:僅把缺少的代碼作為答案,千萬不要填寫多余的代碼、符號或說明文字!!

答案:evaluate(x + v1.n + 1)

 

7、錯誤票據

某涉密單位下發了某種票據,並要在年終全部收回。

每張票據有唯一的ID號。全年所有票據的ID號是連續的,但ID的開始數碼是隨機選定的。

因為工作人員疏忽,在錄入ID號的時候發生了一處錯誤,造成了某個ID斷號,另外一個ID重號。

你的任務是通過編程,找出斷號的ID和重號的ID。

假設斷號不可能發生在最大和最小號。

要求程序首先輸入一個整數N(N<100)表示后面數據行數。
接着讀入N行數據。
每行數據長度不等,是用空格分開的若干個(不大於100個)正整數(不大於100000)
每個整數代表一個ID號。

要求程序輸出1行,含兩個整數m n,用空格分隔。
其中,m表示斷號ID,n表示重號ID

例如:
用戶輸入:
2
5 6 8 11 9
10 12 9

則程序輸出:
7 9


再例如:
用戶輸入:
6
164 178 108 109 180 155 141 159 104 182 179 118 137 184 115 124 125 129 168 196
172 189 127 107 112 192 103 131 133 169 158
128 102 110 148 139 157 140 195 197
185 152 135 106 123 173 122 136 174 191 145 116 151 143 175 120 161 134 162 190
149 138 142 146 199 126 165 156 153 193 144 166 170 121 171 132 101 194 187 188
113 130 176 154 177 120 117 150 114 183 186 181 100 163 160 167 147 198 111 119

則程序輸出:
105 120

 

思路:這題主要難點在於輸入的處理,可以使用getline搭配for循環進行輸入,然后使用stringstream把輸入的數據都讀入一維的整型數組中。

      要掌握istringstream的用法,使用istringstream的構造函數綁定一個string對象后,可以從中以空格或回車為分隔符讀出任意類型的數據。

注意: 當cin和getline同時出現時,在cin之后一定要使用cin.get()讀掉cin之后的回車,否則該回車將會被后面的getline讀取,導致意想不到的錯誤結果。

 1 #include <iostream>
 2 #include <algorithm>
 3 #include <string>
 4 #include <sstream>
 5 
 6 using namespace std;
 7 
 8 int a[110];        //用來保存讀入的整數
 9 int N;
10 
11 int main()
12 {
13     cin >> N;     //N為行數
14     string s;
15     cin.get();    //讀掉cin>>N時輸入的回車,以免被之后的getline讀掉,導致錯誤
16 
17     int j = 0;
18     for (int i = 1; i <= N; ++i)
19     {
20         string line;
21         getline(cin, line);
22         istringstream is(line);
23         while (is >> a[j])  //把整數全部存在數組中,可以從istringstream中讀出任意類型
24             j++;
25     }
26 
27     sort(a, a + j);  //對數組重新排序
28 
29     int m, n;
30     for (int i = 0; i < j - 1; ++i)
31     {
32         if (a[i + 1] == a[i])
33             n = a[i];        //重復的
34 
35         if (a[i + 1] == a[i] + 2)
36             m = a[i] + 1;    //缺少的
37     }
38 
39     cout << m << ' ' << n << endl;
40 
41 
42     return 0;
43 
44 }

 

Java解法:

 1 import java.util.ArrayList;
 2 import java.util.Collections;
 3 import java.util.Scanner;
 4 
 5 public class Main {
 6     public static void main(String[] args) {
 7         Scanner sc = new Scanner(System.in);
 8         int N; // N表示輸入數據的行數
 9         N = sc.nextInt();
10         
11         sc.nextLine();  //去除讀入N時流里面殘留的回車符 12         
13         String s;
14         ArrayList<Integer> al = new ArrayList<Integer>();
15         for (int i = 0; i < N; ++i) { // 讀入N行數據
16             s = sc.nextLine();
17             s = s.trim();    //去除字符串首尾空白字符,注意trim()函數的使用方法
18             String[] sArr = s.split("\\s+"); // 將每行的數據按一個或多個空格分割
19             for (String str : sArr) {
20                 al.add(Integer.valueOf(str)); // 將分割后的String轉換成Integer后再存入ArrayList
21             }
22         }
23 
24         Collections.sort(al);
25         int m = 0, n = 0;
26         for (int i = 0; i < al.size() - 1; ++i) {    //注意此處要轉換成int進行比較,不能直接用Integer比較,不然會出錯
27             if (al.get(i + 1).intValue() == al.get(i).intValue())
28                 n = al.get(i); // 重號ID
29             if (al.get(i).intValue()+1 < al.get(i+1).intValue())
30                 m = al.get(i) + 1; // 斷號ID
31         }
32 
33         System.out.println(m + " " + n);
34 
35     }
36 
37 }

 

8、買不到的數目

小明開了一家糖果店。他別出心裁:把水果糖包成4顆一包和7顆一包的兩種。糖果不能拆包賣。

小朋友來買糖的時候,他就用這兩種包裝來組合。當然有些糖果數目是無法組合出來的,比如要買 10 顆糖。

你可以用計算機測試一下,在這種包裝情況下,最大不能買到的數量是17。大於17的任何數字都可以用4和7組合出來。

本題的要求就是在已知兩個包裝的數量時,求最大不能組合出的數字。

輸入:
兩個正整數,表示每種包裝中糖的顆數(都不多於1000)

要求輸出:
一個正整數,表示最大不能買到的糖數

不需要考慮無解的情況

例如:
用戶輸入:
4 7
程序應該輸出:
17

再例如:
用戶輸入:
3 5
程序應該輸出:
7

看到本題后感覺無從下手,只好瞎猜,猜想結果應該小於m*n, 於是一寫程序,竟然發現過了,O(∩_∩)O哈哈~

 1 #include <iostream>
 2 #include <cstring>
 3 
 4 using namespace std;
 5 
 6 int vis[100000];  //開一個盡量大一點的數組
 7 
 8 int main()
 9 {
10     memset(vis, 0, sizeof(vis));
11     int m, n;
12     cin >> m >> n;
13 
14     //猜想結果小於m*n, 結果還真猜對了
15     for (int i = 0; i <= m; ++i)
16         for (int j = 0; j <= n; ++j)
17             vis[i*n + j*m] = 1;
18         
19 
20     for (int i = m*n; i > m; --i)
21     {
22         if (vis[i] != 1)
23         {
24             cout << i << endl;
25             break;
26         }
27     }
28 
29     return 0;
30 }

  


免責聲明!

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



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