2020.04.11
这题其实正常思路还可以,有点类似b站李永乐老师讲的方法。
核心还是寻找子问题,dp[i][j]
表示一共有i层j个鸡蛋。可以把dp[i][j]
分为先在第m层扔一次,在剩下的结果里继续分析。dp[i][j] = min(dp[i][j], max(dp[m][j-1], dp[N-m][j]))
第一个min
对应最优解,第二个max
表示最差的情况。
这么分析的话复杂度为 O ( K N 2 ) O(KN^2) O(KN2)
仔细观察其实选择第一次扔的楼层这一问题也是可以优化的,我们希望得到一个最佳的第一次扔楼层。想一下,从顶层扔和从底层扔其实一样,都剩下N-1的楼层需要去进一步验证。我们希望得到的是第一次扔完以后最坏情况和最好情况的最接近。此时最坏情况是最下的。
class Solution(object):
def superEggDrop(self, K, N):
"""
:type K: int
:type N: int
:rtype: int
"""
dp = [[float('inf')]*(K+1) for _ in range(N+1)] # [N][K] 楼层最低0,鸡蛋最少1
for i in range(K+1): # 第1层,无论多少鸡蛋都扔一次(0) 第0层不用扔
dp[1][i] = 1
dp[0][i] = 0
for i in range(N+1): # 只有1个鸡蛋有几层扔几次
dp[i][0] = i
dp[i][1] = i
for i in range(2, K+1): # 蛋
for j in range(2,N+1): ## 层
left = 1
right = j
while left<=right:
k = (right+left)>>1
broken = dp[k-1][i-1]
notbroken = dp[j-k][i]
if notbroken>broken:
left = k+1
elif notbroken<broken:
right = k-1
else:
break
dp[j][i] = max(broken,notbroken)+1
return dp[-1][-1]
作为对比我贴上自上而下的带有备忘录的方法。
class Solution:
def superEggDrop(self, K: int, N: int) -> int:
memo = {}
def dp(k, n):
if (k, n) not in memo:
if n == 0:
ans = 0
elif k == 1:
ans = n
else:
left, right = 1, n
while left <= right:
mid = (left + right)>>1
t1 = dp(k-1, mid-1)
t2 = dp(k, n-mid)
if t1 < t2:
left = mid+1
elif t1 > t2:
right = mid-1
else:
break
ans = max(dp(k-1, mid-1), dp(k, n-mid))+1
memo[k, n] = ans
return memo[k, n]
return dp(K, N)
还有一种更加巧妙的方法。我就放上链接不再讨论了。