Leetcode #319: 灯泡开关

题目

题干

该问题灯泡 题面:

There are n bulbs that are initially off. You first turn on all the bulbs, then you turn off every second bulb.
On the third round, you toggle every third bulb (turning on if it’s off or turning off if it’s on). For the ith round, you toggle every i bulb. For the nth round, you only toggle the last bulb.
Return the number of bulbs that are on after n rounds.

初始时有 n 个灯泡处于关闭状态。

对某个灯泡切换开关意味着:如果灯泡状态为关闭,那该灯泡就会被开启;而灯泡状态为开启,那该灯泡就会被关闭。

第 1 轮,每个灯泡切换一次开关。即,打开所有的灯泡。

第 2 轮,每两个灯泡切换一次开关。即,每两个灯泡关闭一个。

第 3 轮,每三个灯泡切换一次开关。

第 i 轮,每 i 个灯泡切换一次开关。而第 n 轮,你只切换最后一个灯泡的开关。

找出 n 轮后有多少个亮着的灯泡。

示例

示例1:
灯泡开关状态

输入:3
输出:1
解释:
最开始三个灯泡的状态是 [灭,灭,灭];
第一次按开关以后变成:[亮,亮,亮];
第二次按开关以后变成:[亮,灭,亮];
第三次按开关以后变成:[亮,灭,灭];

示例2:

输入:n = 0
输出:0

示例3:

输入:n = 1
输出:1

提示:

  • 0 < = n < = 1 0 9 0 <= n <= 10^9 0<=n<=109

题解

问题转化为求1~n有多少个数字的约数个数为奇数个。

解法一

可以反着求每个数的约数个数。对于1~n中的某一个数i,枚举它的所有倍数,计算他对它的倍数约数个数的贡献。

时间复杂度:

(n/1)+(n/2)+(n/3)+…+(n/n)=nlogn

Python

class Solution:
    def __init__(self, n):
        self.n = n

   def bulb_switch(self):
        h = [0 for i in range(self.n)]  # h[i]存储编号i+1灯泡被操作的次数  
        for i in range(1, self.n+1):  # 第i轮操作
            j = 1 # 灯泡编号
            while i * j <= self.n:
                h[i * j-1] += 1
                j += 1
        res = 0
        for i in range(self.n):
            if h[i] % 2 == 1:  # 操作次数为奇数则灯泡亮着
                res += 1
        return res

解法二

结论: 一个数字的约数个数是奇数个等价于这个数字是完全平方数。

证明必要性:
如果n是完全平方数,那么除了√n × √n,其余的约数对均包含两个数字,因此完全平方数有奇数个约数。

证明充分性:
约数个数的问题常常会和质因子分解结合在一起,设:
n = p 1 a 1 + p 1 a 2 + . . . + p k a k n = p_1^{a_1}+p_1^{a_2}+...+p_k^{a_k} n=p1a1+p1a2+...+pkak
因此约数个数为(a1+1)×…×(ak+1),又因为约数个数为奇数个,因此a1…ak都必须是偶数,所以n可以写作:
n = ( p 1 a 1 / 2 × p 2 a 2 / 2 × … × p k a k / 2 ) 2 n=(p_1^{a_1/2}×p_2^{a_2/2}×…×p_k^{a_k/2})^2 n=(p1a1/2×p2a2/2××pkak/2)2
因此n是完全平方数。
证明了这个结论之后原问题就等价于1~n中有多少个完全平方数。

Python

from math import sqrt


class Solution:
    def bulb_switch(self, n):
        return int(sqrt(n))
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值