题目
You are given K eggs, and you have access to a building with N floors from 1 to N.
Each egg is identical in function, and if an egg breaks, you cannot drop it again.
You know that there exists a floor F with 0 <= F <= N such that any egg dropped at a floor higher than F will break, and any egg dropped at or below floor F will not break.
Each move, you may take an egg (if you have an unbroken one) and drop it from any floor X (with 1 <= X <= N).
Your goal is to know with certainty what the value of F is.
What is the minimum number of moves that you need to know with certainty what F is, regardless of the initial value of F?
Example 1:
Input: K = 1, N = 2
Output: 2
Explanation:
Drop the egg from floor 1. If it breaks, we know with certainty that F = 0.
Otherwise, drop the egg from floor 2. If it breaks, we know with certainty that F = 1.
If it didn’t break, then we know with certainty F = 2.
Hence, we needed 2 moves in the worst case to know what F is with certainty.
Example 2:
Input: K = 2, N = 6
Output: 3
Example 3:
Input: K = 3, N = 14
Output: 4
思路
这题已经超出我的理解范围了,花了半个多小时才读懂题目意思,实在不容易。拾人牙慧,参考大神写的解题过程可能比较好理解。
[1]经典动态规划:高楼扔鸡蛋
[2]LeetCode:887. Super Egg Drop - Python
分别用两种不同的状态方程来表示的。
思路一
状态dp[k][n]:k 个鸡蛋,n 层楼找到 F的最少操作次数。
状态转移方程:max(dp(k-1,i-1)+1,dp(k,n-i)+1)
选择在第 i 层楼扔鸡蛋,之后,可能出现两种情况:鸡蛋碎了,鸡蛋没碎。
如果鸡蛋碎了,那么鸡蛋的个数 K 应该减一,剩下 k - 1 个鸡蛋,此时说明 F 在楼下(i 层的下面),接下来还要进行操作 dp[i-1][k-1] 次(子问题)。
如果鸡蛋没碎,那么鸡蛋的个数 K 不变,搜索的楼层区间应该变为 [i+1…N] 共 N-i 层楼。
python 代码
class Solution(object):
def superEggDrop(self, K, N):
"""
:type K: int
:type N: int
:rtype: int
"""
memo = dict()
def dp(k,n):
if k == 1:
return n
if n == 0:
return 0
if (k,n) in memo: # 查找
return memo[(k,n)]
res = float("inf")
for i in range(1,n+1):
res = min(res,max(dp(k-1,i-1)+1,dp(k,n-i)+1))
memo[(k,n)] = res # 记录
return res
return dp(K,N)
时间复杂度为O(K*N^2), 空间复杂度 O(KN)。
此时运行超时,可将线性搜索改进为二分搜索,参考1
二分改进
def superEggDrop(self, K: int, N: int) -> int:
memo = dict()
def dp(K, N):
if K == 1: return N
if N == 0: return 0
if (K, N) in memo:
return memo[(K, N)]
res = float('INF')
# 用二分搜索代替线性搜索
lo, hi = 1, N
while lo <= hi:
mid = (lo + hi) // 2
broken = dp(K - 1, mid - 1) # 碎
not_broken = dp(K, N - mid) # 没碎
# res = min(max(碎,没碎) + 1)
if broken > not_broken:
hi = mid - 1
res = min(res, broken + 1)
else:
lo = mid + 1
res = min(res, not_broken + 1)
memo[(K, N)] = res
return res
return dp(K, N)
时间复杂度为 O(KNlogN), 空间复杂度为 O(KN)。
思路二
状态dp[m][k]:k 个鸡蛋, m次操作(扔m次),可以判定的最大楼层数是dp[m][k]。
状态转移方程:dp[m][k] = dp[m - 1][k] + dp[m - 1][k - 1] + 1
如果当前鸡蛋碎了, 此时能判断出的楼下的层数最少为 dp[m - 1][k - 1] 。
如果当前鸡蛋没碎,此时能判断出的楼上的层数最多为 dp[m - 1][ k ] 。
无论是上楼还是下楼,总的楼层数 = 楼上的楼层数 + 楼下的楼层数 + 1(当前这层楼),所以总体就是上面的方程式了。参考2
python代码
dp = [[0] * (K + 1) for i in range(N + 1)]
for m in range(1, N + 1):
for k in range(1, K + 1):
dp[m][k] = dp[m - 1][k - 1] + dp[m - 1][k] + 1
if dp[m][K] >= N:
return m
状态压缩
dp = [0] * (K + 1)
m = 0
while dp[K] < N:
for k in range(K, 0, -1):
dp[k] = dp[k - 1] + dp[k] + 1
m += 1
return m