灯泡开关

灯泡开关

在这个问题中,我们能够首先想到的就是使用暴力模拟。根据模拟可以直接模拟每一步的操作。但是这会发生TLE错误,分析时间复杂度。第一次会进行n次操作,第二次进行n/2次操作,第三次进行n/3次操作......因此总的时间复杂度为
其中C为欧拉常数,为0.5772...,虽然看起来这个只是一个的时间复杂度,但是在10000000的时候就会TLE。
使用代码如下:

public int bulbSwitch(int n){
        // 有效区间1到n+1,便于模拟
        int[] bulbs = new int[n+1];
        for(int i = 1;i <= n;i++){
            int j = 1;
            int k = j*i;
            while (k <= n){
                bulbs[k] = bulbs[k]^1;  //使用异或操作,安慰自己这样会快一些不会发生TLE
                j++;
                k = j*i;
            }
        }
        int res = 0;
        for(int i = 1;i <= n;i++){
            res += bulbs[i]==0 ? 0:1;
        }
        return res;
    }

因此,我们应该像一个方法来解决超时的问题,一般解决思路是空间换时间,另一种是使用数学方法或找规律。
现在我们在n=5的时候进行模拟,我们用1表示开,0表示关,可以看到以下结果:

  • 初始状态 0 0 0 0 0
  • 第一次时 1 1 1 1 1
  • 第二次时 1 0 1 0 1
  • 第三次时 1 0 0 0 1
  • 第四次时 1 0 0 1 1
  • 第五次时 1 0 0 1 0
    经过观察,可以发现现在只有1和4是打开的,所以我们从数学的方向来观察该结果,1和4都是平方数,因此是不是平方数的位置就一定是打开的呢?
    在这个题目里面,因为灯泡是改变状态,也就是关掉的会被打开,打开的会被关闭。因此所有会被改变偶数次的灯泡会变成关闭状态,被改变奇数次的灯泡会变成打开状态。那我们怎么计算灯泡被改变了偶数次还是奇数次呢?在第n个灯泡,只有一个数字是n的因数的时候才能改变n的位置的灯泡状态。当n为4的时候,他的因数为(1,4),(2,2)。因此在1,2,4的时候会改变位置为n的灯泡的状态,同时改变的也是奇数次,最后结果为灯泡开。因此这个问题变成了前n个数字中有多少个完全平方数。因此可以使用下面的方式来解决:
public int bulbSwitch(int n){
        int res = 0;
        while (res * res <= n) res++;
        return res;
    }
//更好的解法
public int bulbSwitch(int n){
        return (int)Math.sqrt(n);
    }

你可能感兴趣的:(灯泡开关)