2015年第六屆藍橋杯總結與思考(C/C++本科B組)
目錄
1.獎券數目
有些人很迷信數字,比如帶“4”的數字,認為和“死”諧音,就覺得不吉利。
雖然這些說法純屬無稽之談,但有時還要迎合大眾的需求。某抽獎活動的獎券號碼是
5位數(10000-99999),要求其中不要出現帶“4”的號碼,主辦單位請你計算一下,如果任何兩張獎券不重號,最多可發出獎券多少張。
**解題思路:**我用了兩種方法一種是將五位數各位上的數字全排列,並且各位上的數字都不為4;另一種是利用for迴圈和while迴圈遍歷10000到99999,得出不含4的數的個數
第一種:
#include<stdio.h>
int main() {
int a, b, c, d, e, n = 0;
for(a = 1; a < 10; a++)
for(b = 0; b < 10; b++)
for(c = 0; c < 10; c++)
for(d = 0; d < 10; d++)
for(e = 0; e < 10; e++) {
if(a != 4 && b != 4 && c != 4 && d != 4 && e != 4)
n++;
}
printf ("%d\n", n);
return 0;
}
第二種我用了兩種方法
第一種是記錄含4的數的個數然後再用99999-10000+1去減
第二種是直接記錄不含4的數的個數
#include<stdio.h>
int main() {
int count = 0;
int x;
for(int i = 10000; i <= 99999; i++) {
int t = i;
while(t) {
x = t % 10;
t /= 10;
if(x == 4){
count++;
break;
}
}
}
printf ("%d\n", 99999 - 10000 - count + 1);
return 0;
}
#include<stdio.h>
int main() {
int count = 0;
int x;
for(int i = 10000; i <= 99999; i++) {
int t = i;
while(t) {
x = t % 10;
t /= 10;
if(x == 4){
break;
}
if(t == 0) {
count++;
}
}
}
printf("%d\n",count);
return 0;
}
2.星系炸彈
在X星系的廣袤空間中漂浮著許多X星人造“炸彈”,用來作為宇宙中的路標。
每個炸彈都可以設定多少天之後爆炸。
比如:阿爾法炸彈2015年1月1日放置,定時為15天,則它在2015年1月16日爆炸。
有一個貝塔炸彈,2014年11月9日放置,定時為1000天,請你計算它爆炸的準確日期。
請填寫該日期,格式為 yyyy-mm-dd 即4位年份2位月份2位日期。比如:2015-02-19
#include<stdio.h>
#define DAY 1000
int main() {
int year = 2014, month = 11, day = 9;
int m[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
for(int i = 0; i < DAY; i++) {
day++;
if( day > m[month]) {
day = 1;
month++;
if(month > 12) {
year++;
month = 1;
}
}
if(year % 4 == 0 && year % 100 != 0 || year % 400 == 0)
m[2] = 29;
else
m[2] = 28;
}
printf("%d-%02d-%02d\n", year, month, day);
return 0;
}
3.三羊獻瑞
觀察下面的加法算式:
····祥 瑞 生 輝
··+三 羊 獻 瑞
-------------------
三 羊 生 瑞 氣
其中,相同的漢字代表相同的數字,不同的漢字代表不同的數字。
請你填寫“三羊獻瑞”所代表的4位數字(答案唯一),不要填寫任何多餘內容。
**解題思路:**在加法運算中進位只能為一所以三代表1,用a-g分別代表祥 瑞 生 輝 羊 獻 氣,祥在最高位上所以不能為零因漢字代表不數字所以祥也補為1,運用七重for迴圈得出“三羊獻瑞”所代表的4位數字
#include<stdio.h>
int main() {
for(int a = 2; a < 10; a++)
for(int b = 0; b < 10; b++)
if(b != 1 && b != a)
for(int c = 0; c < 10; c++)
if(c != 1 && c != a && c != b)
for(int d = 0; d < 10; d++)
if(d != 1 && d != a && d != b && d != c)
for(int e = 0; e < 10; e++)
if(e != 1 && e != a && e != b && e != c && e != d)
for(int f = 0; f < 10; f++)
if(f != 1 && f != a && f != b && f != c && f != d && f != e)
for(int g = 0; g < 10; g++){
if(g != 1 && g != a && g != b && g != c && g != d && g != e && g != f) {
int n = a * 1000 + b * 100 + c * 10 + d,
m = 1 * 1000 + e * 100 + f * 10 + b,
x = 1 * 10000 + e * 1000 + c * 100 + b * 10 + g;
if(n + m == x)
printf("%d\n", m);
}
}
return 0;
}
4.格子中輸出
StringInGrid函式會在一個指定大小的格子中列印指定的字串。
要求字串在水平、垂直兩個方向上都居中。
如果字串太長,就截斷。
如果不能恰好居中,可以稍稍偏左或者偏上一點。
下面的程式實現這個邏輯,請填寫劃線部分缺少的程式碼。
#include <stdio.h>
#include <string.h>
void StringInGrid(int width, int height, const char* s)
{
int i,k;
char buf[1000];
strcpy(buf, s);
if(strlen(s)>width-2) buf[width-2]=0;
printf("+");
for(i=0;i<width-2;i++) printf("-");
printf("+\n");
for(k=1; k<(height-1)/2;k++){
printf("|");
for(i=0;i<width-2;i++) printf(" ");
printf("|\n");
}
printf("|");
printf("%*s%s%*s",_____________________________________________); //填空
printf("|\n");
for(k=(height-1)/2+1; k<height-1; k++){
printf("|");
for(i=0;i<width-2;i++) printf(" ");
printf("|\n");
}
printf("+");
for(i=0;i<width-2;i++) printf("-");
printf("+\n");
}
int main()
{
StringInGrid(20,6,"abcd1234");
return 0;
}
對於題目中資料,應該輸出:
解題思路:
scanf裡用*修飾符,是起到過濾讀入的作用。比如一個有三列數值的資料,我只想得到第2列數值,可以在迴圈裡用scanf(“%*d%d%d”, a[i])來讀入第i行的第2個數值到a[i]。
* 修飾符在printf中的含義完全不同。例如printf(“%6d”, 123),這是設定域寬的意思。同理,%6s也是域寬。 修飾符正是用來更靈活的控制域寬。
使用%*s,表示這裡的具體域寬值由後面的實參決定,如printf(“%*s”, 6, “abc”)就是把”abc”放到在域寬為6的空間中右對齊。
明白了 * 是用變數來控制域寬,那麼這題就簡單了,這裡應該填寫5個實參。然後字元長度的計算應該用buf而不是s,因為buf才是截斷後的長度,用s的話,如果s長度超過了width-2,效果就不對了。
(width-strlen(buf)-2)/2,"",buf, (width-1-strlen(s))/2, ""
5.九陣列分數
1,2,3…9 這九個數字組成一個分數,其值恰好為1/3,如何組法?
下面的程式實現了該功能,請填寫劃線部分缺失的程式碼。
#include <stdio.h>
void test(int x[])
{
int a = x[0]*1000 + x[1]*100 + x[2]*10 + x[3];
int b = x[4]*10000 + x[5]*1000 + x[6]*100 + x[7]*10 + x[8];
if(a*3==b) printf("%d / %d\n", a, b);
}
void f(int x[], int k)
{
int i,t;
if(k>=9){
test(x);
return;
}
for(i=k; i<9; i++){
{t=x[k]; x[k]=x[i]; x[i]=t;}
f(x,k+1);
_____________________________________________ // 填空處
}
}
int main()
{
int x[] = {1,2,3,4,5,6,7,8,9};
f(x,0);
return 0;
}
解題思路:
本題考查的是回溯,演算法是 一共九個數,在第一個數選定之後,選定第二個數,再選定第三個數……
橫線處附近的問題在於如果避免當前選定的數字不與之前已經選定的重複。
常見的方法有兩種
1.交換
2.記錄陣列法
本題中題目為了避免重複給出的是交換法。
常見的格式為for(i = k; i < 9; i++){t = x[k];x[k] = x[i];x[i] = t;回溯;還原;}
解釋: i = 0時會將第一個數(k=0)和後面的數(i從0到9)依次交換(x[k]與x[i]交換),
每完成這樣一次交換後會呼叫下一位(k+1)的交換,這時i = 1會將第二個數(k=1)和後面的數(i從1到9)一次交換……
{t=x[k]; x[k]=x[i]; x[i]=t;}
6.加法變乘法
我們都知道:1+2+3+ … + 49 = 1225
現在要求你把其中兩個不相鄰的加號變成乘號,使得結果為2015
比如:
1+2+3+…+10 * 11+12+…+27 * 28+29+…+49 = 2015
就是符合要求的答案。
請你尋找另外一個可能的答案,並把位置靠前的那個乘號左邊的數字提交(對於示例,就是提交10)。
解題思路:
由題可知2015-(1+2+3+…+10 * 11+12+…+27 * 28+29+…+49 )- 10 * 11 - 11 * 12=1225-(1+2+3+ … + 49 )- 2* 10 - 2 * 11 - 2
#include<stdio.h>
int main() {
int i, j;
for(i = 1; i < 47; i++) {
for(j = i + 2; j <= 48; j++) {
int x = 1225 - i * 2 - j * 2 - 2;
int y = 2015 - i * (i + 1) - j * (j + 1);
if(x == y && i != 10){
printf("%d\n",i);
}
}
}
return 0;
}
7.牌型種數
小明被劫持到X賭城,被迫與其他3人玩牌。
一副撲克牌(去掉大小王牌,共52張),均勻發給4個人,每個人13張。
這時,小明腦子裡突然冒出一個問題:
如果不考慮花色,只考慮點數,也不考慮自己得到的牌的先後順序,自己手裡能拿到的初始牌型組合一共有多少種呢?
一種暴力,一種dfs
#include <stdio.h>
int main () {
int ans = 0;
for (int a = 0; a < 5; a++)
for (int b = 0; b < 5; b++)
for (int c = 0; c < 5; c++)
for (int d = 0; d < 5; d++)
for (int e = 0; e < 5; e++)
for (int f = 0; f < 5; f++)
for (int g = 0; g < 5; g++)
for (int h = 0; h < 5; h++)
for (int i = 0; i < 5; i++)
for (int j = 0; j < 5; j++)
for (int k = 0; k < 5; k++)
for (int l = 0; l < 5; l++)
for (int m = 0; m < 5; m++)
if ((a + b + c + d + e + f + g + h + i + j + k + l + m ) == 13)
ans++;
printf ("%d", ans);
return 0;
}
#include<stdio.h>
int num = 0, sum = 0;
void DFS(int cut) { //cut為手中牌數
if (sum>13)return;
if (cut == 13) {
if (sum == 13)num++;
return;
}
else {
for (int i = 0; i < 5; i++) {
sum += i;
DFS(cut + 1);
sum -= i; //還原狀態
}
}
}
int main() {
DFS(0);
printf("%d", num);
}
8.移動距離
X星球居民小區的樓房全是一樣的,並且按矩陣樣式排列。其樓房的編號為1,2,3…
當排滿一行時,從下一行相鄰的樓往反方向排號。
比如:當小區排號寬度為6時,開始情形如下:
1 2 3 4 5 6
12 11 10 9 8 7
13 14 15 …
我們的問題是:已知了兩個樓號m和n,需要求出它們之間的最短移動距離(不能斜線方向移動)
輸入為3個整數w m n,空格分開,都在1到10000範圍內
w為排號寬度,m,n為待計算的樓號。
要求輸出一個整數,表示m n 兩樓間最短移動距離。
例如:
輸入:
6 8 2
輸出:
4
再例如:
輸入:
4 7 20
輸出:
5
#include<stdio.h>
#include<math.h>
void F(int w, int n, int& x, int& y) {
x = (n-1) / w + 1;
y = n % w;
if (y == 0) y = w;
if (x % 2 == 0) { // 偶數行倒著數
y = w - y + 1;
}
}
int main() {
int w, m, n;
int x1, y1, x2, y2;
scanf("%d%d%d", &w, &m, &n);
F(w, m, x1, y1);
F(w, n, x2, y2);
printf("%d\n", abs(x1-x2) + abs(y1-y2));
return 0;
}
9.壘骰子
賭聖atm晚年迷戀上了壘骰子,就是把骰子一個壘在另一個上邊,不能歪歪扭扭,要壘成方柱體。
經過長期觀察,atm 發現了穩定骰子的奧祕:有些數字的面貼著會互相排斥!
我們先來規範一下骰子:1 的對面是 4,2 的對面是 5,3 的對面是 6。
假設有 m 組互斥現象,每組中的那兩個數字的面緊貼在一起,骰子就不能穩定的壘起來。
atm想計算一下有多少種不同的可能的壘骰子方式。
兩種壘骰子方式相同,當且僅當這兩種方式中對應高度的骰子的對應數字的朝向都相同。
由於方案數可能過多,請輸出模 10^9 + 7 的結果。
不要小看了 atm 的骰子數量哦~
「輸入格式」
第一行兩個整數 n m
n表示骰子數目
接下來 m 行,每行兩個整數 a b ,表示 a 和 b 數字不能緊貼在一起。
「輸出格式」
一行一個數,表示答案模 10^9 + 7 的結果。
「樣例輸入」
2 1
1 2
「樣例輸出」
544
10.生命之樹
在X森林裡,上帝建立了生命之樹。
他給每棵樹的每個節點(葉子也稱為一個節點)上,都標了一個整數,代表這個點的和諧值。
上帝要在這棵樹內選出一個非空節點集S,使得對於S中的任意兩個點a,b,都存在一個點列 {a, v1, v2, …, vk, b} 使得這個點列中的每個點都是S裡面的元素,且序列中相鄰兩個點間有一條邊相連。
在這個前提下,上帝要使得S中的點所對應的整數的和儘量大。
這個最大的和就是上帝給生命之樹的評分。
經過atm的努力,他已經知道了上帝給每棵樹上每個節點上的整數。但是由於 atm 不擅長計算,他不知道怎樣有效的求評分。他需要你為他寫一個程式來計算一棵樹的分數。
「輸入格式」
第一行一個整數 n 表示這棵樹有 n 個節點。
第二行 n 個整數,依次表示每個節點的評分。
接下來 n-1 行,每行 2 個整數 u, v,表示存在一條 u 到 v 的邊。由於這是一棵樹,所以是不存在環的。
「輸出格式」
輸出一行一個數,表示上帝給這棵樹的分數。
「樣例輸入」
5
1 -2 -3 4 5
4 2
3 1
1 2
2 5
「樣例輸出」
8