【题目】开灯关灯问题

题目

有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<<'\t'; 
        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判断。 || 是满足短路求值的,因此性能不会损失
    
    */


}

 

posted @ 2017-04-09 18:52  lulipro  阅读(1226)  评论(0编辑  收藏  举报