1. 程式人生 > >藍橋杯-程式碼填空之四

藍橋杯-程式碼填空之四

身份證校驗—神祕三位數—生日相同概率—四方定理—因數分解—組合數

①身份證校驗

如果讓你設計個程式,用什麼變數儲存身份證號碼呢?長整數可以嗎?不可以!
因為有人的身份證最後一位是"X"
實際上,除了最後一位的X,不會出現其它字母!
身份證號碼18位 = 17位 + 校驗碼
校驗碼的計算過程:
例如:身份證前17位 = ABCDEFGHIJKLMNOPQ

A~Q 每位數字乘以權值求和(每位數字和它對應的“權”相乘後累加)
17位對應的權值分別是:
7  9 10 5 8 4 2 1 6 3 7 9 10 5 8 4 2


求出的總和再對11求模
然後按下表對映:
餘數 0 1 2 3 4 5 6 7 8 9 10
校驗碼: 1 0 X 9 8 7 6 5 4 3 2 

char verifyCode(char* s)
{
	static int weight[] = {7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2};
	static char map[] = {'1','0','X','9','8','7','6','5','4','3','2'};

	int sum = 0;
	for(int i=0; i<17; i++)
	{
		sum += (______________) * weight[i];  // 填空
	}

	return map[____________];  // 填空
}

題目中已經清楚說明,要做什麼了,分成兩步,第一步求權值,求權值的總和,然後對11求模,

根據餘數進行相應對映,唯一注意一點就是,進來的字元肯定不是整型,需要減去48(即‘0’).

答案:s[i] - '0'                   sum % 11

②神祕的三位數

有這樣一個3位數,組成它的3個數字階乘之和正好等於它本身。即:abc = a! + b! + c!
下面的程式用於搜尋這樣的3位數。

int JC[] = {1,1,2,6,24,120,720,5040,40320,362880};
	int i;
	for(i=100; i<1000; i++)
	{
		int sum = 0;
		int x = i;
		while(__________)
		{
			sum += JC[x%10];
			x /= 10;
		}
		if(i==sum) printf("%d\n", i);
	}

題意很清晰,解法也明瞭,用JC陣列存0!~9!,判斷符不符合條件,肯定要一位數一位數取出來,

那就像判斷水仙花數一樣,%10再/10的迴圈。

答案:x>0

③生日相同的概率

生活中人們往往靠直覺來進行粗略的判斷,但有的時候直覺往往很不可靠。比如:如果你們班有30名同學,那麼出現同一天生日的概率有多大呢?你可能不相信,這個概率高達70%左右。
以下的程式就是用計算機隨機模擬,再統計結果。

#define N 30
	......
	int a[N];
	srand( time( NULL ) );
	int n = 0;
	for(int k=0; k<10000; k++)
	{
		for(int i=0; i<N; i++)
			a[i] = rand() % 365;
		bool tag = false; // 假設沒有相同
		for(i=1; i<N; i++)
		{
			for(int j=0; j<i; j++)
			{
				if(a[i]==a[j]) 
				{
					tag = true;
					break;
				}
			}
			_____________________;
		}
		if(tag) n++;
	}

	printf("%f\n", 1.0 * n / 10000 * 100);

30個人,同一天生日概率高達70%,( ⊙o⊙ )哇!

言歸正傳,解題。這道題,其實我沒怎麼大看,就看到當tag為true時,n++,但是當tag為true時候,雖然break,裡面有兩層for,於是乎,恍然大悟鳥。。。有時候解題並非要全弄懂他的方法,比賽時間有限啊= 。=

答案: if(tag) break;

④四方定理

數論中有著名的四方定理:所有自然數至多隻要用四個數的平方和就可以表示。
我們可以通過計算機驗證其在有限範圍的正確性。
對於大數,簡單的迴圈巢狀是不適宜的。下面的程式碼給出了一種分解方案。

int f(int n, int a[], int idx)
{
	if(______________) return 1;  // 填空1
	if(idx==4)  return 0;

	for(int i=(int)sqrt(n); i>=1; i--)
	{
		a[idx] = i;

		if(_______________________)  return 1;  // 填空2
	}

	return 0;
}

int main(int argc, char* argv[])
{
	for(;;)
	{
		int number;
		printf("輸入整數(1~10億):");
		scanf("%d",&number);
		
		int a[] = {0,0,0,0};

		int r = f(number, a, 0);

		printf("%d: %d %d %d %d\n", r, a[0], a[1], a[2], a[3]);
		
	}

	return 0;
}
大體一看程式碼結構,就知道,得,又是一道遞迴題目,遞迴方法,也不難理解,就是,原來數減去一個數的平方,再接著找,找的範圍,也不用我們想,題目給了。遞迴結束條件,出了Idx肯定是n了,當n為0,代表找到了,不多說了,return 1。遞迴結束條件定是定了,可是,還有一個return 1,怎麼辦啊?

其實不難,仔細一想就明白了,for迴圈在找i,找到以後,肯定要向下遞迴下去,那肯定是要執行f函數了,執行f函式會返回值的,一個return0,一個return1,什麼時候return1呢?必然就是當它返回1時,不斷return1,回到主函式。

答案:n==0     f(n-i*i, a, idx+1) == 1

⑤因數分解

因數分解是十分基本的數學運算,應用廣泛。下面的程式對整數n(n>1)進行因數分解。

比如,n=60, 則輸出:2 2 3 5。請補充缺失的部分。

void f(int n)
{
	for(int i=2; i<n/2; i++)
	{
		____________________
		{
			printf("%d ", i);
			n = n / i;
		}
	}
	if(n>1) printf("%d\n", n);
}

因數分解,簡單吧?剛學語言的時候,肯定做過這種題目,藍橋杯出的程式碼填空,對於我們大部分做過的題目,一般就會讓我們來優化一下或者簡潔一下程式碼,不可能,填我們原來做的時候的程式碼。

這道題,顯然也是,你會發現i是一直在++的,例子中也出現了,有可能有兩個2的情況。怎麼回事呢?

我以前做用的是兩個for,然後,找到以後i--,使得i再次為原值,進行判斷。

除了for迴圈就剩下while了,所以咯,答案一躍而出啊。

答案:while(n%i==0)

⑥組合數

從4個人中選2個人參加活動,一共有6種選法。
從n個人中選m個人參加活動,一共有多少種選法?下面的函式實現了這個功能。

// n 個元素中任取 m 個元素,有多少種取法
int f(int n, int m)
{
	if(m>n) return 0;
	if(m==0) _______________;

	return f(n-1,m-1) + _____________;
}

就是排列組合的問題嘛,4個人選2個,就是C四二。

n個人選m個就是Cnm

當m==0?  C30==C33==1咯,所以return 1

第二個空如果想不起來,你不妨看一下判斷條件,出現m>n return 0,輸入的內容不會有m>n的情況吧?

所以只能是,我們遞迴的時候出現啦。

答案:   return 1      f(n-1,m)