- 参考网址1 github - 鸡蛋掉落讨论
- 参考网址2 双蛋问题 - 李永乐
思路
dp[k][m]表示k个鸡蛋,m次移动可以测出的最大层数
状态转移公式:
dp[k][m] = dp[k-1][m-1] + dp[k][m-1] + 1
解释:
Step1: 考虑在哪一层丢第一个鸡蛋(dp[k-1][m-1]+1层 )
// 为什么要选择在dp[k-1][m-1]+1层丢鸡蛋?
反证法:
- 更低楼层丢鸡蛋。最终得到的并不是“最大”楼层数,未充分挖掘鸡蛋数和移动次数的潜力,最终求解时会剩余一定量的鸡蛋或移动次数。
- 更高楼层丢鸡蛋。不妨假设高了一层,即在第dp[k-1][m-1]+2层丢鸡蛋。若蛋碎,则可排除该层以上的所有楼层;若蛋未碎,因为剩下的k-1个鸡蛋在m-1次移动只能求出dp[k-1][m-1]的楼层,而现在剩余的楼层却是dp[k-1][m-1]+1,得出矛盾!
Step2: 对于第一个鸡蛋状态的讨论:
- 蛋碎:可用k-1个鸡蛋用m-1步测出下面的dp[k-1][m-1]层楼,此时,总共可以测出无限∞的楼层。(如果蛋碎,那么无论上面还有多少层,都是这个结果,所以可以测出无限层 )
- 蛋未碎:说明所求楼层数不在下面的dp[k-1][m-1]层楼中;此时,还剩k个鸡蛋和m-1次移动,可用dp[k-1][m-1]+1以上的楼层继续测试;总共可以测出dp[k-1][m-1]+dp[k][m-1]+1层楼。
step3: 得出最多可测的楼层数:
dp[k][m] = max(dp[k-1][m-1]+dp[k][m-1]+1, ∞)
,即,
dp[k][m] = dp[k-1][m-1]+dp[k][m-1]+1
代码实现:
class Solution:
def superEggDrop(self, K: int, N: int) -> int:
dp = [[0 for i in range(N+1)] for i in range(K+1)]
# 注意:dp = [[0] * (N+1)] * (K+1) # Wrong!!!
for m in range(1, N+1):
for k in range(1,K+1):
dp[k][m] = dp[k-1][m-1] + dp[k][m-1] + 1
if dp[k][m] >= N:
return m
return N
代码优化:
# 参考背包问题
class Solution:
def superEggDrop(self, K: int, N: int) -> int:
dp = [0] * (K+1)
m = 0
while 1:
m += 1
for k in range(K, 0, -1):
dp[k] = dp[k-1] + dp[k] + 1
if dp[k] >= N:
return m
return N