曾在豆瓣上看到過一個小朋友貼出他自己的代碼(http://www.douban.com/group/topic/40293109/),當時隨口指點了幾句。難得這位小朋友虛心修正、從善如流,不斷地改,又不斷地貼,堅持了很久。到后來這位小朋友的代碼已經大有長進。
這位小朋友犯過的很多錯誤都非常典型,在初學者中非常普遍,於是整理了一下,應該對其他初學者有借鑒意義。
問題
開燈問題
有n盞燈,編號為1~n,第1個人把所有燈打開,第2個人按下所有編號為2 的倍數的開關(這些燈將被關掉),第3 個人按下所有編號為3的倍數的開關(其中關掉的燈將被打開,開着的燈將被關閉),依此類推。一共有k個人,問最后有哪些燈開着?輸入:n和k,輸出開着的燈編號。k≤n≤1000
代碼:
1 #include <stdio.h> 2 #include <math.h> 3 4 int main() 5 { 6 int a[1001],n,k,i,j; 7 8 printf("請分別輸入燈和人的數量\n"); 9 scanf("%d%d",&n,&k); 10 11 while(1) //檢驗是否超出 12 { 13 if(k>=1 && k<=1000 && n>=k && n<=1000) 14 break; 15 else 16 { 17 printf("數值不符,請重新輸入:\n"); 18 scanf("%d%d",&n,&k); 19 } 20 } 21 22 for(i=2;i<=k;i++) //每操作一次第i*j個開關,a[i*j]加1 23 { 24 for(j=1;i*j<=n;j++) 25 { 26 a[i*j]=a[i*j]+1; 27 } 28 } 29 30 for(i=1;i<=n;i++) //若操作次數為偶數,表示該位置的燈亮着 31 { 32 if(a[i]%2==0 && a[i]!=0) 33 printf("%d\t",i); 34 } 35 36 printf("\n"); 37 return 0; 38 }
測試:
“自己測了覺得沒問題..在線系統結果是WrongAnswer ..未找出原因..”
評:
最明顯的錯誤就是第26行
a[i*j]=a[i*j]+1;
由於前面定義a數組為局部auto類別,在不進行初始化的情況下,a中的數據是垃圾值。換句話說,a中的數據是無意義的。因此a[i*j]+1這個表達式沒有意義。
此外,第22行
for(i=2;i<=k;i++) //每操作一次第i*j個開關,a[i*j]加1
在邏輯上也是錯誤的,缺乏“第1個人把所有燈打開”的步驟。
另外
printf("請分別輸入燈和人的數量\n"); scanf("%d%d",&n,&k); while(1) //檢驗是否超出 { if(k>=1 && k<=1000 && n>=k && n<=1000) break; else { printf("數值不符,請重新輸入:\n"); scanf("%d%d",&n,&k); } }
這段寫得很丑,屬於典型的譚浩強風格,C語言應該這樣寫:
printf("請分別輸入燈和人的數量\n"); while(scanf("%d%d",&n,&k) ,( k <1 || n<k || n>1000) ) { printf("數值不符,請重新輸入:\n"); }
代碼中還有其他一些毛病,由於不是最主要的問題,這里就不再進一步指出了。
重構:
#include <stdio.h> #define MAXNUM 1000 #define ON 0 #define OFF 1 int main( void ) { int light[MAXNUM] = { ON } ; //把所有燈打開 int n , k ; int i ; printf( "請分別輸入燈和人的數量\n" ); while ( scanf("%d%d", & n ,& k ) ,( k < 1 || n < k || n > MAXNUM ) ) printf("數值不符,請重新輸入:\n"); for ( i = 2 - 1 ; i < k ; i ++ ) //第2個人按下所有編號為2 的倍數的開關…… { int j ; for ( j = i ; j < n ; j += i + 1) light[j] = ! light[j]; } for ( i = 0 ; i < n ; i ++ ) if( light[i]==ON ) printf( "%d " , i + 1 ); putchar('\n'); return 0; }