一道有趣的算法题。
看《算法设计与分析基础》第三版的时候,1.1节习题中又这么一道题:
带锁的门:在走廊上有n个带锁的门,从1到n一次编号。最初所有的门都是关着的。我们从门前经过n次,每次都从1号门开始。在第i次经过时(i=1,2,3,…,n)我们改变i的整数倍号锁的状态:如果门是关着的,就打开它;如果门是打开的,就关上它。在最后一次经过,哪些门是打开的,哪些门是关上的?有多少打开的门?
一开始没什么头绪,不过我们可以写个例子看看,比如取n等于10。
n/门号 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
2 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 |
3 | 1 | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 0 |
4 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 0 | 0 |
5 | 1 | 0 | 0 | 1 | 0 | 1 | 1 | 1 | 0 | 1 |
6 | 1 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 0 | 1 |
7 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 0 | 1 |
8 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 1 |
9 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 1 |
10 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 0 |
n为0表示初始状态,0表示关闭,1表示打开。
在写这个例子的时候,我们会发现门号如果能被k(第k次经过)整除,该门的状态就会发生变化,当k>$\sqrt{k}$时,只会改变第k个门的状态。
我接着就能发现状态改变偶数次后,状态不变,也就是说,对于第k个门,如果k有偶数个约数,那么这扇门的状态最后还是和初始状态一样,这样我们的问题就是找出哪些数有偶数个约数。
我们观察上面的例子,发现1,4和9的最后状态是与初始状态不一样外,其他门的状态都一样。到这里,我们可能很容易就猜想到1,4,9等这些是某个数的平方的数有奇数个约数,当然,我们可以取n的值16,25再看看。能想到这里,其实为什么这些数有奇数个约数也很容易想到。
如果k是n的一个约数,那么n除以k等于$\frac nk$余0,那么$\frac nk$也是n的一个约数,这样,n的约数一般都是成对出现,只有一种情况就是k等于$\frac nk$的时候,即这两个约数相等,那么这时只算一个约数。而只有像1,4,9这些数会出现这种情况,1比较特别。
这样也就回答了上面的那个问题。
下面是代码演示:
1 | public static void main(String[] args) { |