1. 程式人生 > >2013第四屆藍橋杯 C/C++本科A組

2013第四屆藍橋杯 C/C++本科A組

今天做了一份題,還是很有收穫的,最大的收穫就是:自己還差得太遠。

1、高斯日記

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

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

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

    高斯出生於:1777年4月30日。
    
    在高斯發現的一個重要定理的日記上標註著:5343,因此可算出那天是:1791年12月15日。

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

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

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

請嚴格按照格式,通過瀏覽器提交答案。
注意:只提交這個日期,不要寫其它附加內容,比如:說明性的文字。

答案:1799-07-16

#include <stdio.h>

int main (void)
{
	int a[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
	int n = 8113, x = 246;
	int mouth, day;
	for(int year = 1778; year <= 1900; year ++)
	{
		int temp;
		if(year % 400 == 0 || year % 4 == 0 && year % 100 != 0)
			temp = x + 366;
		else
			temp = x + 365;
		if(temp < n)
		{
			x = temp;
		}
		else if(temp > n)
		{
			break;
		}
	}
	for(int j = 1; j <= 12; j++)
	{
		if(x + a[j] <= n)
		{
			x += a[j];
		}
		else 
		{
			mouth = j;
			break;
		}
	}
	day = n - x;

		
	printf("%d %d %d\n", year, mouth, day);
	return 0;
}
2、 排它平方數

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

    原來,203879 * 203879 = 41566646641

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

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

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

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

請通過瀏覽器提交答案。
注意:只提交另一6位數,題中已經給出的這個不要提交。
注意:不要書寫其它的內容(比如:說明性的文字)。

答案:

639172

剛開始做的時候i沒有設定成——int64,結果得數超過了int型,如果i是int型的,計算mul的時候要記得強制型別轉換。錯誤有點低階。。。寫下來,給自己提個醒。

//深搜
#include <stdio.h>
#include <string.h>

int change(__int64 x, __int64 y)
{
	bool vis[10];
	memset(vis, 0, sizeof(vis));
	while (x)
	{
		if(vis[x % 10] == 1)
		{
			return 0;
		}
		vis[x % 10] = 1;
		x /= 10;
	}

	while (y)
	{
		if(vis[y % 10] == 1)
		{
			return 0;
		}
		y /= 10;
	}
	return 1;
}


int main (void)
{
	__int64 i;
	__int64 mul;
	for(i = 123456; i < 987654; i++)
	{
		mul = i * i;
		if(change(i, mul))
		{
			printf("%I64d\n", mul);
			printf("%I64d\n", i);
		}
	}
	return 0;
}

3、 振興中華

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

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

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


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


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

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

答案是一個整數,請通過瀏覽器直接提交該數字。
注意:不要提交解答過程,或其它輔助說明類的內容。

答案:35

這個題我直接用排列組合算的。。。

答案為C(3, 7)      從起點走到終點,總共要走7步,而且只能向下走和向右走,選其中的3步向下走,那麼其餘的4步向右走。

也附上程式碼:

#include<stdio.h>
#include<string.h>

int main()
{
    int i,j,a[4][5];
    for(i = 0; i <= 3; i++)
        a[i][0] = 1;
    for(j = 0; j <= 4; j++)
        a[0][j] = 1;
    
    for(i = 1; i <= 3; i++)
        for(j = 1; j <= 4; j++)
            a[i][j] = a[i][j - 1] + a[i - 1][j];//a[i][j]儲存走到a[i][j]位置可能的路線有幾種

    printf("%d\n", a[3][4]);
    return 0;
}



4、 顛倒的價牌


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

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

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

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

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

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

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

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


答案是一個4位的整數,請通過瀏覽器直接提交該數字。
注意:不要提交解答過程,或其它輔助說明類的內容。


答案:9088

#include <stdio.h>

int a[10] = {0, 1, 2, -1, -1, 5, 9, -1, 8, 6};
int b[4];//儲存排列出來的4位數
int c[100] = {0}, m = 0;//儲存符合條件的虧本價格的數值
int d[100] = {0}, n = 0;//儲存賺的價格所賺的錢
int sub[100] = {0};//儲存虧本的價格所虧的錢

void f1()
{
	int i;
	int num1 = 0, num2 = 0;
	if(b[3] == 0)
		return ;
	for(i = 0; i < 4; i++)
	{
		num1 = num1 * 10 + b[i];
	}
	for(i = 3; i >= 0; i--)
	{
		if(a[b[i]] != -1)
			num2 = num2 * 10 + a[b[i]];
		else
			return ;
	}
	if((num1 - num2) >= 200 && (num1 - num2) < 300)	
	{
		c[m] = num1;
		sub[m++] = num1 - num2;
	}
	else if((num2 - num1) >= 800 && (num2 - num1) < 900)
	{
		d[n ++] = num2 - num1;
	}
	return ;
}

void dfs(int n)
{
	int i;
	if(n == 4)
	{
		f1();
		return;
	}
	for(i = 0; i < 10; i++)
	{
		b[n] = i;
		dfs(n + 1);
	}
}

int main (void)
{
	int i, j;
	dfs(0);
	for(i = 0; i < m; i++)
	{
		for(j = 0; j < n; j++)
		{
			if(d[j] - sub[i] == 558)
			{
				printf("%d\n", c[i]);
				break;
			}
		}
	}
	return 0;
}



題目標題:字首判斷

    如下的程式碼判斷 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(________*haystack ++ != *needle ++________) return NULL;  //填空位置
    }
    
    if(*needle) return NULL;
    
    return haystack_start;
}

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


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 = __________evaluate(x + v1.n + 1)___________________;
    //我的理解是:v1往下的遞迴已經找到了第一個數字,v2要從下一個v1沒有遞迴過的位置開始遞迴,而v1.n就是v1已經遞迴過的長度
    
    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;
}

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

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
   

資源約定:
峰值記憶體消耗 < 64M
CPU消耗  < 1000ms


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

所有程式碼放在同一個原始檔中,除錯通過後,拷貝提交該原始碼。

注意: main函式需要返回0
注意: 只使用ANSI C/ANSI C++ 標準,不要呼叫依賴於編譯環境或作業系統的特殊函式。
注意: 所有依賴的函式必須明確地在原始檔中 #include <xxx>, 不能通過工程設定而省略常用標頭檔案。

提交時,注意選擇所期望的編譯器型別。

這一題主要的問題就是每一行輸入的數字數目是不確定的,所以一行一行的讀入字串,再將字串中的數字分離出來儲存到陣列中。接下來遍歷一遍陣列,找出兩個數就可以了。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>

int cmp (const void *a, const void *b)
{
	return (*(int *)a - *(int *)b);
}

int main (void)
{
	int n, i;

	while(scanf("%d", &n) != EOF)
	{
		getchar();
		char a[100];
		int x[100000];
		int m = 0;
		while (n--)
		{//將字串中的數分離出來
			int temp = 0;
			memset(a, 0, sizeof(a));
			gets(a);
			int len = strlen(a);
			for(i = 0; i < len; i++)
			{
				if(isdigit(a[i]))
				{
					temp = temp * 10 + (a[i] - 48);
				}
				else if(a[i] == ' ')
				{
					x[m ++] = temp;
					temp = 0;
				}
			}
			x[m ++] = temp;
			//因為最後一個數字的後面沒有其他的字元了,不加這一句就沒有辦法讀入最後一個數字
		}
	
		qsort(x, m, sizeof(x[0]), cmp);
	
		int p, q;
		for(i = 1; i < m; i++)
		{
			if(x[i] == x[i - 1])
				p = x[i];
			if(x[i] == x[i - 1] + 2)
			{
				q = x[i] - 1;
			}
		}
		printf("%d %d\n", q, p);
	}
	return 0;
}


8、買不到的數目

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

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

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

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

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

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

不需要考慮無解的情況

例如:
使用者輸入:
4 7
程式應該輸出:
17

再例如:
使用者輸入:
3 5
程式應該輸出:
7


資源約定:
峰值記憶體消耗 < 64M
CPU消耗  < 3000ms


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

所有程式碼放在同一個原始檔中,除錯通過後,拷貝提交該原始碼。

注意: main函式需要返回0
注意: 只使用ANSI C/ANSI C++ 標準,不要呼叫依賴於編譯環境或作業系統的特殊函式。
注意: 所有依賴的函式必須明確地在原始檔中 #include <xxx>, 不能通過工程設定而省略常用標頭檔案。

提交時,注意選擇所期望的編譯器型別。

我最初想到的方法是,設定一個標記陣列,通過深搜將所有可以表示的數目都標記上,然後再從後往前找到第一個沒有被標記的數就是所要找的數,但是這方法效率太低了。最後用的方法是參考了別人的程式碼。。。。
假設輸入數字為n,m(n<m),那麼,若存在由x開始的n個連續的數,x、x+1、……、x+n-1,則可以得到最大不能組合的數字為x-1,因為後邊的所有的數都可以由x+n,x+1+n,…………,x+n-1+n,求出,所以只要求出n即可。

#include <stdio.h>
#include <string.h>
bool vis[10000000];

int main (void)
{
	int n, m;
	while (scanf("%d %d", &n, &m) != EOF)
	{
		if(n > m)
		{
			int temp = n;
			n = m;
			m = temp;
		}
		int i, num = 0;//num用來計算是不是連續出現n個數都能用所給的兩個數表示
		vis[0] = 1;//這個別忘,不然沒有結果
		for(i = 1; i < 10000000; i++)
		{
			if(i >= n && vis[i - n])
				vis[i] = 1;
			else if(i >= m && vis[i - m])
				vis[i] = 1;
			if(vis[i]) num++;
			else
				num = 0;
			if(num == n)
			{
				printf("%d\n", i - n);
				break;
			}
		}
		memset(vis, 0, sizeof(vis));
	}
	return 0;
}

10、大臣的旅費



    很久以前,T王國空前繁榮。為了更好地管理國家,王國修建了大量的快速路,用於連線首都和王國內的各大城市。

    為節省經費,T國的大臣們經過思考,制定了一套優秀的修建方案,使得任何一個大城市都能從首都直接或者通過其他大城市間接到達。同時,如果不重複經過大城市,從首都到達每個大城市的方案都是唯一的。

    J是T國重要大臣,他巡查於各大城市之間,體察民情。所以,從一個城市馬不停蹄地到另一個城市成了J最常做的事情。他有一個錢袋,用於存放往來城市間的路費。

    聰明的J發現,如果不在某個城市停下來修整,在連續行進過程中,他所花的路費與他已走過的距離有關,在走第x千米到第x+1千米這一千米中(x是整數),他花費的路費是x+10這麼多。也就是說走1千米花費11,走2千米要花費23。

    J大臣想知道:他從某一個城市出發,中間不休息,到達另一個城市,所有可能花費的路費中最多是多少呢?

輸入格式:
輸入的第一行包含一個整數n,表示包括首都在內的T王國的城市數
城市從1開始依次編號,1號城市為首都。
接下來n-1行,描述T國的高速路(T國的高速路一定是n-1條)
每行三個整數Pi, Qi, Di,表示城市Pi和城市Qi之間有一條高速路,長度為Di千米。

輸出格式:
輸出一個整數,表示大臣J最多花費的路費是多少。

樣例輸入:
5
1 2 2
1 3 1
2 4 5
2 5 4

樣例輸出:
135

樣例說明:
大臣J從城市4到城市5要花費135的路費。


根據資源限制儘可能考慮支援更大的資料規模。


資源約定:
峰值記憶體消耗 < 64M
CPU消耗  < 5000ms


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

所有程式碼放在同一個原始檔中,除錯通過後,拷貝提交該原始碼。

注意: main函式需要返回0
注意: 只使用ANSI C/ANSI C++ 標準,不要呼叫依賴於編譯環境或作業系統的特殊函式。
注意: 所有依賴的函式必須明確地在原始檔中 #include <xxx>, 不能通過工程設定而省略常用標頭檔案。

提交時,注意選擇所期望的編譯器型別。

開始的時候,我還一直在想用搜索圖的方法來解,想的太複雜了。。。。其實二維陣列就能搞定。。。。哎,還是要多多練習啊。

/*
這一題其實根本不用搜索,處理二維陣列就可以了,
只要找出所有路徑中長度最長的就可以了。
通過更新二維陣列,使得每個能連通的城市之間的距離都儲存在陣列的相應位置
再遍歷陣列找到最長的距離即可
*/
#include<stdio.h>
#include<string.h>
int a[1000][1010];

int main()
{
    int i, j, k, n, x, y, z, max, sum;
    scanf("%d", &n);
	
	for(i = 1; i <= n; i++)
	{
		for(j = 1; j <= n; j++)
		{
			a[i][j] = 999999;
			//注意要將陣列初始化為比較大的數,因為下面要比較距離,
			//如果都設定為0,最後陣列會全部都變成0,結果出錯
		}
	}

    for(i = 1; i < n; i++)
    {
        scanf("%d %d %d", &x, &y, &z);
        a[x][y] = z;
        a[y][x] = z;
    }
    max = sum = 0;
    for(k = 1; k <= n; k++)
        for(i = 1; i <= n; i++)
            for(j = 1; j <= n;j++)
            if(i != j)
            {//比較是直接到達距離更短還是通過其他城市到達距離更短,
	     //前提是所有不能直接到達的城市之間都記錄為一個比較大的數,也就是上面的999999
                    a[i][j] = a[i][k] + a[k][j] < a[i][j] ? a[i][k] + a[k][j] : a[i][j];
            }
	/*
	for(i = 1; i <= n; i++)
	{
		for(j = 1; j <= n; j++)
		{
			printf("%d  ", a[i][j]);
		}
		printf("\n");
	}
	*/
    for(i = 0; i <= n; i++)
        for(j = 0; j <= n; j++)
            if(a[i][j] != 999999)
            max = a[i][j] > max ? a[i][j] : max;
    for(i = 1; i <= max; i++)
        sum += (i + 10);
    printf("%d\n", sum);
    return 0;
}

再加把勁吧!!