1. 程式人生 > >BNUZ-ACM 2018國慶新生歡樂賽部分題解+思路(已解出答案部分)

BNUZ-ACM 2018國慶新生歡樂賽部分題解+思路(已解出答案部分)

由於時間不足,本人在新生賽僅瀏覽了四題,其中的兩題完全通過。 下面是已解出的一題,供比對和取優。

A. 三角戀

下面貼上原題: 在這裡插入圖片描述 在這裡插入圖片描述 我的思路: 首先需要一個while(scanf)迴圈,以供測試器迴圈測試各組資料。然後因為需要輸入T組資料,所以還需要一個if迴圈或者while迴圈。資料的輸入也需要利用到到陣列,比如a[i]。再者,輸出時需要判斷是第幾次輸出,輸出一個可自增的量(%d,c++)。最後,需要一個判斷if以決定輸出嚶嚶嚶還是苦海無涯。 貼上我的程式碼,我們把它叫做程式碼a:

#include<stdio.h>
int main(){
	int a,b=0,c,d,i;
	while(scanf("%d",&a) !=EOF){		
		while(a--){
			int g=0;
			b++;
			scanf("%d",&c);
			int d[c+1];
			for(i=1;i<=c;i++){
				scanf("%d",&d[i]);
			}
				int f=0;
			while(c--){
				f++;
				if(d[d[d[f]]]==f){
					printf("Case #%d: 苦海無涯\n",b);
					g=1;	
					break;
				}	
			}
			if(g==0){
				printf("Case #%d: 嚶嚶嚶\n",b);	
			}
		}
	}
	return 0;
} 

出題者提供的程式碼,我們把它叫做程式碼b:

#include<stdio.h>
int main() {
	int T, n;
	int a[5005];
	scanf("%d", &T);
	int cas = 1;
	while(T--) {
		scanf("%d", &n);
		int flag = 0;
		for(int i = 1; i <= n; i++) {
			scanf("%d", &a[i]);
		}
		for(int i = 1; i <= n; i++) {
			if(a[a[a[i]]] == i){
				flag = 1;
				break;
			}
		}
		if(flag){
			printf("Case #%d: 苦海無涯\n",cas++);
		}else {
			printf("Case #%d: 嚶嚶嚶\n",cas++);
		}
	}
	return 0;
}

在這裡對比一下兩段程式碼,以及寫一下他們所用到的知識。

輸入階段:

程式碼a:

	while(scanf("%d",&a) !=EOF){	//輸入一個數字,決定總程式要迴圈多少次
		while(a--){					//while(0)為結束
			int g=0;
			b++;					//b代表是第幾次迴圈,決定輸出case幾
			scanf("%d",&c);			//輸入一個數字代表人數
			int d[c+1];				//定義陣列,輸入數字的值決定了要定義多少個數組
			for(i = 1; i <= c; i++)
				scanf("%d",&d[i]);	//迴圈給陣列賦值,直到結束
		}
	}

程式碼b:

	int a[5005];						//直接定義5005個數組
	scanf("%d", &T);
	while(T--) {						//同樣迴圈T次
		scanf("%d", &n);
		int flag = 0;
		for(int i = 1; i <= n; i++) {	//迴圈賦值
			scanf("%d", &a[i]);
		}

這兩段程式碼的區別在於a程式碼沒有直接定義陣列的個數,b直接定義了有5005個數字,根據題目來看,其實兩種方法都可以。 while(0)代表while(false)程式將不執行,當while裡面的值仍然是非零數的時候,程式會繼續迴圈。

計算判斷階段:

程式碼a:

		int f=0;					//每次迴圈先將f定義為0,以方便後面判斷
		while(c--){
			f++;					//每次迴圈自增,判斷各人是否符合條件
			if(d[d[d[f]]]==f){
				printf("Case #%d: 苦海無涯\n",b);
				g=1;				//g=1是為了跳過下方嚶嚶嚶的判斷
				break;				//跳出語句,不必要重複執行,減少執行時間
			}	
		}
		if(g==0)					//如果不曾有過進入上方的if,則會輸出嚶嚶嚶
			printf("Case #%d: 嚶嚶嚶\n",b);	

程式碼b:

		int cas = 1;
		for(int i = 1; i <= n; i++) {
			if(a[a[a[i]]] == i){
				flag = 1;
				break;
			}
		}
		if(flag){									//if(flag),flag為真時,也就是等於1時進入苦海無涯
			printf("Case #%d: 苦海無涯\n",cas++);	//cas自增,由於兩種情況都會輸出cas,所以沒有問題
		}else {
			printf("Case #%d: 嚶嚶嚶\n",cas++);
		}
	}

在這裡講一下,為什麼是a[a[a[i]]] == i呢 我們假設有一個數組是:

1 2 3 4
3 1 2 2

在這個數組裡面,很明顯是有三角戀的,因為1喜歡3,3喜歡2,2喜歡1。 我們把陣列看成是一種函式:

x 1 2 3
f(x) 3 1 2

那麼這個表格就可以變成:

x 1 f(f(1)) f(1)
f(x) f(1) f(f(f(1))) f(f(1))

那麼問題就變成了f(f(f(1)))是不是等於1的問題了,同樣可以推理出可以變成f(f(f(x)))是不是等於x的問題。

程式碼部分分為兩種方式,個人認為第二種方法更好。程式碼b減少了巢狀,將函式功能模組化,便於維護與理解。同時,有良好的程式碼習慣作為基礎,這些都是我需要學習的。目前來看,我應該在程式碼的整潔性以及規範性上面下一些功夫。