2013年藍橋杯省賽C組筆記

猜年齡(簡單枚舉)

美國數學家維納(N.Wiener)智力早熟,11歲就上了大學。他曾在1935~1936年應邀來中國清華大學講學。一次,他參加某個重要會議,年輕的臉孔引人注目。因而有人詢問他的年齡,他回答說:「我年齡的立方是個4位數。我年齡的4次方是個6位數。這10個數字正好包含了從0到9這10個數字,每一個都剛好出現1次。ios

請你推算一下,他當時到底有多年輕。經過瀏覽器,直接提交他那時的年齡數字。注意:不要提交解答過程,或其它的說明文字。c++

代碼:算法

#include<iostream>
using namespace std;
int main(){
	for(int i=10;i<50;i++){
		int _3=i*i*i;
		int _4=_3*i;
		if(_3>=1000&&_3<10000&&_4>=100000&&_4<1000000){
			printf("%d %d %d\n",i,_3,_4);
		}
	}
	return 0;
}
//輸出結果爲:
//18 5832 104976
//19 6859 130321
//20 8000 160000
//21 9261 194481

因爲這是填空題,只要能算出結果便可,先把可能的結果輸出,根據判斷年齡爲18的時候,0-9每一個數字只出現1次符合條件。編程

馬虎的算式(枚舉+驗證)

小明是個急性子,上小學的時候常常把老師寫在黑板上的題目抄錯了。數組

有一次,老師出的題目是:36 x 495 = ?瀏覽器

他卻給抄成了:396 x 45 = ?函數

但結果卻很戲劇性,他的答案居然是對的!!spa

由於 36 * 495 = 396 * 45 = 17820設計

相似這樣的巧合狀況可能還有不少,好比:27 * 594 = 297 * 54code

假設 a b c d e 表明1~9不一樣的5個數字(注意是各不相同的數字,且不含0

能知足形如: ab * cde = adb * ce 這樣的算式一共有多少種呢?

請你利用計算機的優點尋找全部的可能,並回答不一樣算式的種類數。

知足乘法交換律的算式計爲不一樣的種類,因此答案確定是個偶數。

答案直接經過瀏覽器提交。
注意:只提交一個表示最終統計種類數的數字,不要提交解答過程或其它多餘的內容。

代碼:

暴力枚舉列出全部可能結果

#include<iostream>
using namespace std;
int main(){
	int ans=0;
	for(int a=1;a<=9;a++){
		for(int b=1;b<=9;b++){
			if(b!=a){//各個數字不相同判斷 
				for(int c=1;c<=9;c++){
					if(c!=a&&c!=b){//各個數字不相同判斷 
						for(int d=1;d<=9;d++){
							if(d!=a&&d!=b&&d!=c){//各個數字不相同判斷 
								for(int e=1;e<=9;e++){
									if(e!=a&&e!=b&&e!=c&&e!=d){//各個數字不相同判斷 
										if((a*10+b)*(c*100+d*10+e)==(a*100+d*10+b)*(c*10+e)){
											ans++;
										}
									}
								}
							}
						}
					}
				}
			}
		}
	}
	cout<<ans<<endl;
	return 0;
}
//輸出結果:142

暴力到沒朋友

填空題應第一考慮可否暴力枚舉,簡單粗暴。

振興中華(遞歸)

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

地上畫着一些格子,每一個格子裏寫一個字,以下所示:

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

要求跳過的路線恰好構成「從我作起振興中華」這句話。

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

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

代碼:

簡單理解就是從「從」到「華」有多少條路能夠走

#include<iostream>
using namespace std;
bool vis[5][5];
int f(int x,int y){
	if(x==3&&y==4) //已經到"華"只有一種
		return 1;
	else if(x==3) //已經到最後一行,只能往右走
		return f(x,y+1);
	else if(y==4) //已經到最後一列,只能往下走
		return f(x+1,y);
	else
		return f(x+1,y)+f(x,y+1);
	
}
int main(){
	cout<<f(0,0);
	return 0;
}
//輸出結果:35

遞歸的方法:找重複、找狀態、找出口

幻方填空(暴力法全排列)

幻方是把一些數字填在方格中,使得行、列、兩條對角線的數字之和都相等。

歐洲最著名的幻方是德國數學家、畫家迪勒創做的版畫《憂鬱》中給出的一個4階幻方。

他把1,2,3,...16 這16個數字填寫在4 x 4的方格中。

如圖所示,即:

表中有些數字已經顯露出來,還有些用?和*代替。

請你計算出? 和 * 所表明的數字。並把 * 所表明的數字做爲本題答案提交。

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

注意:不要提交解答過程,或其它輔助說明類的內容。

代碼:

已經使用的數字:1,9,11,13,16

未使用的數字:2,3,4,5,6,7,8,10,12,14,15

由於每一個空只能填一個數字,每一個數字只能填一次,故使用暴力法,求未使用的數字進行全排列,即它們可能組成的各類順序,依次填入方格中進行判斷,當知足條件時返回便可。

在c++中有相應的全排列公式next_permutation(),其它語言須要手動實現。

#include<iostream>
#include<vector>
#include<algorithm> 
using namespace std;
int a[]={2,3,4,5,6,7,8,10,12,14};

void check(vector<int> arr){
	int r1=16+arr[0]+arr[1]+13;
	int r2=arr[2]+arr[3]+11+arr[4];
	int r3=9+arr[5]+arr[6]+arr[7];
	int r4=arr[8]+15+arr[9]+1;
	
	int c1=16+arr[2]+9+arr[8];
	int c2=arr[0]+arr[3]+arr[5]+15;
	int c3=arr[1]+11+arr[6]+arr[9];
	int c4=13+arr[4]+arr[7]+1;
	
	int l=16+arr[3]+arr[6]+1;
	int r=13+11+arr[5]+arr[8];
	if(r1==r2&&r2==r3&&r3==r4&&r4==c1&&c1==c2&&c2==c3&&c3==c4&&c4==l&&l==r){
		cout<<arr[7]<<endl;
	}
}

int main(){
	vector<int> arr;
	for(int i=0;i<10;i++){
		arr.push_back(a[i]);
	}
	do{
		check(arr); 
	}while(next_permutation(arr.begin(),arr.end()));
	return 0;
}

公約數公倍數

咱們常常會用到求兩個整數的最大公約數和最小公倍數的功能。
下面的程序給出了一種算法。
函數 myfunc 接受兩個正整數a,b
通過運算後打印出 它們的最大公約數和最小公倍數。
此時,調用 myfunc(15,20)

將會輸出:
5
60

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

// 交換數值
void swap(int *a,int *b)
{
   int temp;
   temp=*a;
   *a=*b;
   *b=temp;
}

void myfunc(int a, int b)  //使用展轉相除法求最大公約數
{
   int m,n,r;  
   if(a<b) swap(&a,&b);
   m=a;n=b;r=a%b;
   while(r!=0)
   {
    a=b;b=r;
    r=a%b;
   }
   printf("%d\n",b);  // 最大公約數 
   printf("%d\n", ____________________________________);  // 最小公倍數 
}
//已知最大公約數b時可使用公式 n*m/b     a,b已經改變,n,m是a,b的拷貝故使用n,m.
//答案:n*m/b

三部排序(快速排序的變體)

通常的排序有許多經典算法,如快速排序、希爾排序等。

但實際應用時,常常會或多或少有一些特殊的要求。咱們不必套用那些經典算法,能夠根據實際狀況創建更好的解法。

好比,對一個整型數組中的數字進行分類排序:

使得負數都靠左端,正數都靠右端,0在中部。注意問題的特色是:負數區域和正數區域內並不要求有序。能夠利用這個特色經過1次線性掃描就結束戰鬥!!

如下的程序實現了該目標。

其中x指向待排序的整型數組,len是數組的長度。

若是給定數組:
25,18,-2,0,16,-5,33,21,0,19,-16,25,-3,0
則排序後爲:
-3,-2,-16,-5,0,0,0,21,19,33,25,16,18,25

請分析代碼邏輯,並推測劃線處的代碼,經過網頁提交
注意:僅把缺乏的代碼做爲答案,千萬不要填寫多餘的代碼、符號或說明文字!!

void sort3p(int* x, int len)
{
    int p = 0;
    int left = 0;    
    int right = len-1;

    while(p<=right){
        if(x[p]<0){
            int t = x[left];
            x[left] = x[p];
            x[p] = t;
            left++;
            p++;
    	}else if(x[p]>0){
            int t = x[right];
            x[right] = x[p];
            x[p] = t;
            right--;
        }
        else{
        	p++;  //填空位置
        }
	}
}

p 指向當前要判斷元素的下邊

left 指向元素的左邊都小於0,right指向元素的右邊都大於0。

核桃的數量(簡單枚舉)

問題描述
小張是軟件項目經理,他帶領3個開發組。工期緊,今天都在加班呢。爲鼓舞士氣,小張打算給每一個組發一袋核桃(據傳言能補腦)。他的要求是:

  1. 各組的核桃數量必須相同
  2. 各組內必須能平分核桃(固然是不能打碎的)
  3. 儘可能提供知足1,2條件的最小數量(節約鬧革命嘛)

輸入格式
輸入包含三個正整數a, b, c,表示每一個組正在加班的人數,用空格分開(a,b,c<30)
輸出格式
輸出一個正整數,表示每袋核桃的數量。
樣例輸入1
2 4 5
樣例輸出1
20
樣例輸入2
3 1 1
樣例輸出2
3

代碼:

簡單理解就是要求這三個數的最小公倍數,使用暴力枚舉,最壞三者的最小公倍數爲a * b * c。

#include<iostream>
using namespace std;
int main(){
	int a,b,c;
	cin>>a>>b>>c;
	for(int i=1;i<=a*b*c;i++){
		if(i%a==0&&i%b==0&&i%c==0){
			cout<<i<<endl;
			break;
		}
	}
	return 0;
}

打印十字圖(先寫死再寫活)

小明爲某機構設計了一個十字型的徽標(並不是紅十字會啊),以下所示:

..$$$$$$$$$$$$$..
..$...........$..
$$$.$$$$$$$$$.$$$
$...$.......$...$
$.$$$.$$$$$.$$$.$
$.$...$...$...$.$
$.$.$$$.$.$$$.$.$
$.$.$...$...$.$.$
$.$.$.$$$$$.$.$.$
$.$.$...$...$.$.$
$.$.$$$.$.$$$.$.$
$.$...$...$...$.$
$.$$$.$$$$$.$$$.$
$...$.......$...$
$$$.$$$$$$$$$.$$$
..$...........$..
..$$$$$$$$$$$$$..

對方同時也須要在電腦dos窗口中以字符的形式輸出該標誌,並能任意控制層數。

輸入1 ,則輸出

..$$$$$..
..$...$..
$$$.$.$$$
$...$...$
$.$$$$$.$
$...$...$
$$$.$.$$$
..$...$..
..$$$$$..

輸入

一個正整數 n (n< 30) 表示要求打印圖形的層數。

輸出

對應包圍層數的該標誌。

樣例輸入

3

樣例輸出

..$$$$$$$$$$$$$.. 
..$...........$.. 
$$$.$$$$$$$$$.$$$ 
$...$.......$...$ 
$.$$$.$$$$$.$$$.$
$.$...$...$...$.$
$.$.$$$.$.$$$.$.$
$.$.$...$...$.$.$
$.$.$.$$$$$.$.$.$
$.$.$...$...$.$.$
$.$.$$$.$.$$$.$.$
$.$...$...$...$.$
$.$$$.$$$$$.$$$.$
$...$.......$...$
$$$.$$$$$$$$$.$$$
..$...........$..
..$$$$$$$$$$$$$..

代碼:

假設最外層爲第一層

n=3時

爲17 * 17的方格;第一行$個數13

n=2時

爲13 * 13的方格; 第一行$個數9

n=1時

爲 9 * 9的方格; 第一行$個數5

L表示左側邊界爲0,R表示右側邊界爲9+4*(n-1)-1 (下標從0開始R的下標須要減1)

寫死後,只輸出最外一層

#include<iostream>
using namespace std;
char arr[9+4*28][9+4*28];
int N; //一共有幾層 
int L=0; //最外層左邊界下標 
int R; //最外層右邊界下標 
void printAll(int left,int right){
	for(int i=left;i<=right;i++){
		for(int j=left;j<=right;j++){
			if(arr[i][j]!='$') arr[i][j]='.';
			cout<<arr[i][j];
		}
		cout<<endl;
	}
}
int main(){
	cin>>N;
	cout<<N<<endl;
	R=9+4*(N-1)-1; //求出最外層右邊界下標 
	
	//處理第一行和最後一行 
	for(int i=2;i<=R-2;i++){
		arr[0][i]='$';
		arr[R][i]='$';
	} 
	//處理第二行和倒數第二行
	arr[1][2]='$'; arr[1][R-2]='$';
	arr[R-1][2]='$'; arr[R-1][R-2]='$';
	//處理第三行和倒數第三行
	arr[2][0]='$';
	arr[2][1]='$';
	arr[2][2]='$';
	arr[2][R]='$';
	arr[2][R-1]='$';
	arr[2][R-2]='$';
	arr[R-2][0]='$';
	arr[R-2][1]='$';
	arr[R-2][2]='$';
	arr[R-2][R]='$';
	arr[R-2][R-1]='$';
	arr[R-2][R-2]='$';
	//處理兩邊
	for(int i=3;i<=R-3;i++){
		arr[i][0]='$';
		arr[i][R]='$';
	} 
	printAll(L,R);
	return 0;
}
輸入:
3
輸出:
..$$$$$$$$$$$$$..
..$...........$..
$$$...........$$$
$...............$
$...............$
$...............$
$...............$
$...............$
$...............$
$...............$
$...............$
$...............$
$...............$
$...............$
$$$...........$$$
..$...........$..
..$$$$$$$$$$$$$..
輸入:
1
輸出:
..$$$$$..
..$...$..
$$$...$$$
$.......$
$.......$
$.......$
$$$...$$$
..$...$..
..$$$$$..

而後找規律,發現每層只是左右邊界不一樣L每次加2,R每次減2,故單獨寫一個方法輸出第n層的圖像

#include<iostream>
using namespace std;
char arr[9+4*28][9+4*28];
int N; //一共有幾層 
int L=0; //最外層左邊界下標 
int R; //最外層右邊界下標 
void printAll(int left,int right){
	for(int i=left;i<=right;i++){
		for(int j=left;j<=right;j++){
			if(arr[i][j]!='$') arr[i][j]='.';
			cout<<arr[i][j];
		}
		cout<<endl;
	}
}
void dealN(int n){
	int l,r; //當前層的左右邊界,由於時方格,l也表示上邊界,r也表示下邊界 
	l=2*(N-n);
	r=R-2*(N-n); 
	//處理第一行和最後一行 
	for(int i=l+2;i<=r-2;i++){
		arr[l][i]='$';
		arr[r][i]='$';
	} 
	//處理第二行和倒數第二行
	arr[l+1][l+2]='$'; arr[l+1][r-2]='$';
	arr[r-1][l+2]='$'; arr[r-1][r-2]='$';
	//處理第三行和倒數第三行
	arr[l+2][l]='$';
	arr[l+2][l+1]='$';
	arr[l+2][l+2]='$';
	arr[l+2][r]='$';
	arr[l+2][r-1]='$';
	arr[l+2][r-2]='$';
	arr[r-2][l]='$';
	arr[r-2][l+1]='$';
	arr[r-2][l+2]='$';
	arr[r-2][r]='$';
	arr[r-2][r-1]='$';
	arr[r-2][r-2]='$';
	//處理兩邊
	for(int i=l+3;i<=r-3;i++){
		arr[i][l]='$';
		arr[i][r]='$';
	} 
}
int main(){
	cin>>N;

	R=9+4*(N-1)-1; //求出最外層右邊界下標 
	
	for(int i=N;i>0;i--){
		dealN(i);
	}
	
	//最後處理中間十字	
	for(int i=2*N;i<2*N+5;i++){
		arr[2*N+2][i]='$';
		arr[i][2*N+2]='$';
	}
	printAll(L,R);
	return 0;
}
輸入3時
..$$$$$$$$$$$$$..
..$...........$..
$$$.$$$$$$$$$.$$$
$...$.......$...$
$.$$$.$$$$$.$$$.$
$.$...$...$...$.$
$.$.$$$.$.$$$.$.$
$.$.$...$...$.$.$
$.$.$.$$$$$.$.$.$
$.$.$...$...$.$.$
$.$.$$$.$.$$$.$.$
$.$...$...$...$.$
$.$$$.$$$$$.$$$.$
$...$.......$...$
$$$.$$$$$$$$$.$$$
..$...........$..
..$$$$$$$$$$$$$..

帶分數(全排列+枚舉「+」號和「/」號的插入位置)

問題描述
100 能夠表示爲帶分數的形式:100 = 3 + 69258 / 714。

還能夠表示爲:100 = 82 + 3546 / 197。

注意特徵:帶分數中,數字1~9分別出現且只出現一次(不包含0)。

相似這樣的帶分數,100 有 11 種表示法。

輸入格式
從標準輸入讀入一個正整數N (N<1000*1000)

輸出格式
程序輸出該數字用數碼1~9不重複不遺漏地組成帶分數表示的所有種數。

注意:不要求輸出每一個表示,只統計有多少表示法!

樣例輸入1
100
樣例輸出1
11
樣例輸入2
105
樣例輸出2
6

分析:

生成1-9這9個數字的全排列,先在可能的位置插入「+」號,而後在可能的位置插入「/」號,再驗證等式。

#include<iostream>
#include<algorithm>
#include<string>
using namespace std;
int main(){
	int n,ans=0;
	cin>>n;
	string s="123456789";
	do{
		for(int i=1;i<=7;i++){ // i:"+"前串的長度 
			string a=s.substr(0,i);
			int inta=atoi(a.c_str());  // string 轉換 int 
			if(inta>=n) break;
			for(int j=1;j<=9-i-1;j++){ // j: "+"和"/"之間串的長度 
				string b=s.substr(i,j);
				string c=s.substr(i+j);// "/"後面的串
				int intb=atoi(b.c_str());
				int intc=atoi(c.c_str());
				if(intb%intc==0&&inta+intb/intc==n) ans++;
			} 
		}
	}while(next_permutation(s.begin(),s.end()));
	cout<<ans<<endl;
	return 0;
}

結果時正確的,可是運行超時,反覆使用substr()函數會消耗大量時間,須要對代碼進行改進。

#include<iostream>
#include<algorithm>
#include<string>
using namespace std;

//本身實現一個函數,經過從pos截取len長度的字符串
int parse(const char *arr,int pos,int len){
	int ans=0;
	int t=1;
	for(int i=pos+len-1;i>=pos;i--){
		ans+=(arr[i]-'0')*t;
		t*=10;
	}
	return ans;
}
int main(){
	int n,ans=0;
	cin>>n;
	string s="123456789";
	do{
		const char *str=s.c_str();
		for(int i=1;i<=7;i++){ // i:"+"前串的長度 
//			string a=s.substr(0,i);
//			int inta=atoi(a.c_str());  // string 轉換 int 
			int inta=parse(str,0,i);
			if(inta>=n) break;
			for(int j=1;j<=9-i-1;j++){ // j: "+"和"/"之間串的長度 
//				string b=s.substr(i,j);
//				string c=s.substr(i+j);// "/"後面的串
//				int intb=atoi(b.c_str());
//				int intc=atoi(c.c_str());
				int intb=parse(str,i,j);
				int intc=parse(str,i+j,9-i-j);
				if(intb%intc==0&&inta+intb/intc==n) ans++;
			} 
		}
	}while(next_permutation(s.begin(),s.end()));
	cout<<ans<<endl;
	return 0;
} 
//完美,測評經過。

剪格子(深搜+回溯+剪枝)

以下圖所示,3 x 3 的格子中填寫了一些整數。

+--*--+--+
|10* 1|52|
+--****--+
|20|30* 1|
*******--+
| 1| 2| 3|
+--+--+--+

咱們沿着圖中的星號線剪開,獲得兩個部分,每一個部分的數字和都是60。

本題的要求就是請你編程斷定:對給定的m x n 的格子中的整數,是否能夠分割爲兩個部分,使得這兩個區域的數字和相等。

若是存在多種解答,請輸出包含左上角格子的那個區域包含的格子的最小數目。

若是沒法分割,則輸出 0。

輸入格式
程序先讀入兩個整數 m n 用空格分割 (m,n<10)。

表示表格的寬度和高度。

接下來是n行,每行m個正整數,用空格分開。每一個整數不大於10000。

輸出格式
輸出一個整數,表示在全部解中,包含左上角的分割區可能包含的最小的格子數目。
樣例輸入1
3 3
10 1 52
20 30 1
1 2 3
樣例輸出1
3
樣例輸入2
4 3
1 1 1 1
1 30 80 2
1 1 1 100
樣例輸出2
10

#include<iostream>
#include<algorithm>
using namespace std;
int m,n,total;
int g[10][10],ans=100;
bool vis[10][10];
int dx[]={1, 0,-1,0};
int dy[]={0,-1, 0,1};
bool in(int x,int y){  //判斷該位置是否合法 
	if(x>=0&&x<=n-1&&y>=0&&y<=m-1){
		return true;
	}else{
		return false;
	}
}
void f(int x,int y,int sum,int cnt){
//	printf("(%d,%d)sum=%d,cnt=%d\n",x,y,sum,cnt);
	if(sum>total/2)	return;
	if(sum==total/2){
		ans=min(ans,cnt);
		return;
	}
	vis[x][y]=true;
	for(int i=0;i<4;i++){
		int tx=x+dx[i];
		int ty=y+dy[i];
		if(in(tx,ty)&&!vis[tx][ty]){
			f(tx,ty,sum+g[x][y],cnt+1);
		}
	}
	vis[x][y]=false;
}
int main(){
	cin>>m>>n;
	for(int i=0;i<n;i++){
		for(int j=0;j<m;j++){
			cin>>g[i][j];
			total+=g[i][j];  //求出全部元素的和 
		}
	}
	f(0,0,0,0);
	cout<<ans<<endl;
	return 0;
}

上面的代碼能夠經過藍橋杯練習系統,但在有些特殊的狀況會執行錯誤,例如:

1 1

1 2

應輸出3,運行結果是0,表示沒法分割。

相關文章
相關標籤/搜索