曾在豆瓣上看到过一个小朋友贴出他自己的代码(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("请分别输入灯和人的数量 "); 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("数值不符,请重新输入: "); 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 ",i); 34 } 35 36 printf(" "); 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("请分别输入灯和人的数量 "); scanf("%d%d",&n,&k); while(1) //检验是否超出 { if(k>=1 && k<=1000 && n>=k && n<=1000) break; else { printf("数值不符,请重新输入: "); scanf("%d%d",&n,&k); } }
这段写得很丑,属于典型的谭浩强风格,C语言应该这样写:
printf("请分别输入灯和人的数量 "); while(scanf("%d%d",&n,&k) ,( k <1 || n<k || n>1000) ) { printf("数值不符,请重新输入: "); }
代码中还有其他一些毛病,由于不是最主要的问题,这里就不再进一步指出了。
重构:
#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( "请分别输入灯和人的数量 " ); while ( scanf("%d%d", & n ,& k ) ,( k < 1 || n < k || n > MAXNUM ) ) printf("数值不符,请重新输入: "); 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(' '); return 0; }