458. Poor Pigs
题目描述
有1000个水桶,有且仅有一个中有毒,其余都装满水。它们看起来一样。如果一头猪喝了有毒的水将在15min死亡。
问在一小时内找出毒水,需要的最少猪的数量。
【推广】有n个水桶,猪喝毒水后将在m分钟内死亡。问在p分钟内找到毒水桶所需的最少猪的数量?
例子
略
思想
(以例子为例)
1) 一只猪在一小时内最多能验多少桶?
0min喝1号桶,15min后没挂再喝2号桶,60min内可以喝 60/15 = 4 次。如果有5桶水,那个只要喝前4桶就只能第5桶是否有毒。
因此一只猪在一小时可以验5桶水。
2)一只猪在一小时内最多能验多少桶?
既然一只猪能验5桶,那么用二维思路,2只猪应该可以验5*5桶:
猪A负责行,猪B负责列,每15分钟试喝一行/一列的所有5桶水,通过2只猪上天的时间能推断出毒水在几行几列。
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17 18 19 20
21 22 23 24 25
3)推到N只猪,则5^N >= 1000,最小的N即为所求。
解法
class Solution(object):
def poorPigs(self, buckets, minutesToDie, minutesToTest):
"""
:type buckets: int
:type minutesToDie: int
:type minutesToTest: int
:rtype: int
"""
import math
times = minutesToTest//minutesToDie + 1
return int(math.ceil(math.log(buckets, times)))
(不调包)
class Solution(object):
def poorPigs(self, buckets, minutesToDie, minutesToTest):
"""
:type buckets: int
:type minutesToDie: int
:type minutesToTest: int
:rtype: int
"""
nbatch = minutesToTest//minutesToDie + 1
pigs = 0
while nbatch ** pigs < buckets:
pigs += 1
return pigs
319. Bulb Switcher
题目描述
n个灯泡初始化时是关着的。
首先,你点亮所有的灯泡;然后,关掉每第二个灯泡(关掉2,4…);第三轮,每第三个灯泡进行切换(如果它是关闭的则开启它,如果它是开启的则关闭它);第i轮,每第i个灯泡进行切换;第n轮,仅切换最后一个灯泡。
问在第n回后还有多少个灯泡是亮着的?
例子
Input: 3
Output: 1
Explanation:
At first, the three bulbs are [off, off, off].
After first round, the three bulbs are [on, on, on].
After second round, the three bulbs are [on, off, on].
After third round, the three bulbs are [on, off, off].
So you should return 1, because there is only one bulb is on.
思想
题意比较难懂。
第二轮,把每第二个灯泡关掉(关掉2,4…);
第三轮,每第三个灯泡进行切换(如果它是关闭的则开启它,如果它是开启的则关闭它,切换3,6…);
第i轮,每第i个灯泡进行切换;第n回,仅切换最后一个灯泡。
暴力解法 - TLE
class Solution(object):
def bulbSwitch(self, n):
"""
:type n: int
:rtype: int
"""
bulbs = [0] * n
for i in range(1, n+1):
if i == 1:
bulbs = [1] * n
elif i == n:
bulbs[-1] = bulbs[-1] ^ 1
else:
j = i
while j <= n:
bulbs[j-1] = bulbs[j-1] ^ 1
j += i
return sum(bulbs)
观察规律
n = 5时,最后一行 - [1, 0, 0, 1, 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]
n = 10时,最后一行 - [1, 0, 0, 1, 0, 0, 0, 0, 1, 0]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
…
[1, 0, 0, 1, 0, 0, 0, 0, 1, 1]
[1, 0, 0, 1, 0, 0, 0, 0, 1, 0]
n = 15时,最后一行 - [1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0]
…
[1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1]
[1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 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 = 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]
即1+2+1+4+1+6+1+8+1+…,其中1+1+1+1+…的和即为所求。
(解法1 - 优化)
设置标志位attach表明当前应该on了。
class Solution(object):
def bulbSwitch(self, n):
"""
:type n: int
:rtype: int
"""
bulbs = on = off = 0
attach = True # on
while bulbs < n:
if attach:
attach = False
bulbs += 1
on += 1
else:
attach = True
off += 2
bulbs += off
return on
(解法2 - 数论)
时间复杂度 - O(1)
每个灯泡开关被按的次数等于它的编号的约数个数。
最终灯泡是亮的,说明编号有奇数个约数。
下面我们证明:一个数有奇数个约数,等价于它是平方数。
证明:
1)对于每个平方数,除了平方根之外,其余所有约数都是成对出现,所以平方数有奇数个约数。
2)由算数基本定理,一个整数 n 可以唯一表示成: n = p 1 k 1 × p 2 k 2 × . . . × p m k m n = {p_1}^{{k_1}} \times {p_2}^{{k_2}} \times ... \times {p_m}^{{k_m}} n=p1k1×p2k2×...×pmkm其中 p 1 , p 2 . . . p m {p_1},{p_2}...{p_m} p1,p2...pm 均是质数, k 1 , k 2 . . . k m {k_1},{k_2}...{k_m} k1,k2...km 均是正整数。
则 n 的约数个数就是 ∏ i = 1 m ( k i + 1 ) \prod\limits_{i = 1}^m {\left( {{k_i} + 1} \right)} i=1∏m(ki+1)。
如果 n有奇数个约数,说明 k 1 + 1 , k 2 + 1... , k m + 1 {k_1} + 1,{k_2} + 1...,{k_m} + 1 k1+1,k2+1...,km+1 都是奇数,从而 k 1 , k 2 , . . . k m {k_1},{k_2},...{k_m} k1,k2,...km 均是偶数。所以 n = ( p 1 k 1 / 2 × p 2 k 2 / 2 × . . . × p m k m / 2 ) 2 n = {\left( {{p_1}^{{k_1}/2} \times {p_2}^{{k_2}/2} \times ... \times {p_m}^{{k_m}/2}} \right)^2} n=(p1k1/2×p2k2/2×...×pmkm/2)2所以 n 是平方数。
共有 n 个灯泡,则从 1 到 n 中共有 ⌊√n⌋个平方数。
class Solution(object):
def bulbSwitch(self, n):
"""
:type n: int
:rtype: int
"""
return int(n**0.5)