题目
有n(n>0)盏灯,编号为1~n,在桌子上排成一排。开始都是熄灭状态。
第1趟,按下编号为1 的倍数的 灯的开关。(第一趟后所有的灯都亮了)
第2趟,按下编号为2 的倍数的 灯的开关。
....
第 i 趟,按下编号为i 的倍数的 灯的开关。
一直到第n趟,结束。
问:n趟过后,哪些灯亮着,哪些熄灭,有多少亮着?
根据问题,很直接的写出代码。
#include<iostream> #include<cassert> #include<cstring> using namespace std; void onoff(size_t n) { int*door = new int[n+1]; memset(door, 0, sizeof(int) * (n + 1)); for (size_t i = 1; i <=n ; i++) // 走n趟 { for (size_t k = i; k <=n; k+=i) //比 i 小的数都不用考虑,因为他们不是 i 的倍数、。 { if (k%i == 0) //如果门k 是 i的整数倍,则改变他的状态 door[k] = !door[k]; } } for (size_t i = 1; i <=n; i++) { cout << door[i] << ' '; } delete[] door; } int main() { for (size_t i = 1; i <= 36; i++) { cout << "n=" << i<<' '; onoff(i); cout << endl; } return 0; }
运行结果
n=1 1 n=2 1 0 n=3 1 0 0 n=4 1 0 0 1 n=5 1 0 0 1 0 n=6 1 0 0 1 0 0 n=7 1 0 0 1 0 0 0 n=8 1 0 0 1 0 0 0 0 n=9 1 0 0 1 0 0 0 0 1 n=10 1 0 0 1 0 0 0 0 1 0 n=11 1 0 0 1 0 0 0 0 1 0 0 n=12 1 0 0 1 0 0 0 0 1 0 0 0 n=13 1 0 0 1 0 0 0 0 1 0 0 0 0 n=14 1 0 0 1 0 0 0 0 1 0 0 0 0 0 n=15 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0 n=16 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0 1 n=17 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0 1 0 n=18 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0 1 0 0 n=19 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 n=20 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 n=21 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 n=22 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 n=23 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 n=24 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 n=25 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 n=26 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 n=27 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 n=28 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 n=29 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 n=30 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 n=31 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 n=32 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 n=33 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 n=34 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 n=35 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 n=36 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1
分析总结
仔细分析后,发现,被按偶数次的灯,最后是熄灭的,而被按奇数次的灯,最后是亮的。而且从运行结果中可以看出规律:最后亮的灯,其编号都是完全平方数。第1盏,第4盏,第9盏...
举几个例子。
7 = 1 x 7 编号为7,在第1 趟和第 7 趟被按下。
30 = 1 x 30 编号为30,在1,30 ,2, 15 , 3 , 10 趟都会被按下。
= 2 x 15
= 3 x 10
1 = 1 x 1 编号为1, 在第 1趟被按下
4 = 1 x 4 编号为4 , 在第1 4 2 趟被按下
= 2 x 2
可以发现,编号是完全平方数,就会被按奇数次。否则会按下偶数次。因此,这个题目转而变为判断哪些数是完全平方数了。
即:编号为完全平方数的灯,最后是亮的,否则就是熄灭的。
判断一个数是否是完全平方数(不考虑负数)
bool isPerfectSquare(unsigned int n) { unsigned int low = (unsigned int)sqrt(n); return (low*low == n) || ((low + 1)*(low + 1) == n); /* 之所以不直接用 low*low==n判断,是因为浮点数的不精确存储。比如 sqrt(25)在某些时候【可能】会返回4.999999 ,取整后变为4了。 因此,以防意外,我们需要再往上增1判断。 || 是满足短路求值的,因此性能不会损失 */ }