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
Note:
1 <= K <= 100
1 <= N <= 10000
第0-5层与5-10层的效果是一样的
https://leetcode.com/problems/super-egg-drop/discuss/159079/Python-DP-from-kn2-to-knlogn-to-kn
Here the dp formula is dp[k][n] = min(1 + max(dp[k - 1][i - 1], dp[k][n - i])) i = 1...n
.
base case:
dp[0][i]=0, i=1...N # no egg, no floor can check
dp[1][i]=i, i=1...N # one egg, check floor from i to 1
dp[j][1]=1, j=1...K # one floor, only check once
First of all, we can come up with simple O(kn^2)
solution as below:
class Solution(object):
def superEggDrop(self, K, N):
"""
:type K: int
:type N: int
:rtype: int
"""
dp=[[0]*(N+1) for _ in range(K+1)]
for i in range(1, N+1):
dp[1][i] = i
dp[0][i] = 0
for j in range(1, K+1):
dp[j][1] = 1
for i in range(2, K+1):
for j in range(2, N+1):
dp[i][j] = float('inf')
for x in range(1, j+1):
res = 1 + max(dp[i-1][j-x], dp[i][x-1])
dp[i][j]=min(dp[i][j], res)
return dp[-1][-1]
It gets TLE. To optimize, we can see for a fixed k
, dp[k][n]
goes up as n
increases. This means dp[i-1][j-x]
will decrease and dp[i][x-1]
will increase as x goes from 1
to j
. The optimal value of x
will be the middle point where the two meets. So to get the optimal x
value for dp[k][n]
at a fixed k
and n
, we can do a binary search for x
from 1
to n
.
This will save the third for-loop for x
from O(n)
to O(logn)
. Total complexity is O(knlogn)
. Using binary seach, we can only do bottom up dp, as shown below:
class Solution(object):
def superEggDrop(self, K, N):
"""
:type K: int
:type N: int
:rtype: int
"""
def dp(k, n):
if k==1:
return n
if n==0:
return 0
if n==1:
return 1
if (k, n) in d:
return d[k, n]
lo, hi=0, n
while lo<hi:
mid=(lo+hi)/2
left, right=dp(k, mid-1), dp(k-1, n-mid)
if left<right:
lo=mid+1
else:
hi=mid
res=1+max(dp(k, lo-1), dp(k-1, n-lo))
d[k, n]=res
return res
d={}
return dp(K, N)
To go one step further, for a fixed k
, we can see the optimal value of x
for each dp[k][n]
will increase as n
increases. Therefore, once we get the optimal x
for dp[k][n]
, we can save current x
value and start the next round of for-loop directly, instead of initiating x
from 0
again. In this way, in the third for-loop, x
will go from 1
to N
only once as j
in the second for-loop goes from 1
to N
. The total time complexity will be O(kn)
.
class Solution(object):
def superEggDrop(self, K, N):
"""
:type K: int
:type N: int
:rtype: int
"""
dp=[[0]*(N+1) for _ in range(K+1)]
for i in range(1, N+1):
dp[1][i] = i
dp[0][i] = 0
for j in range(1, K+1):
dp[j][1] = 1
for i in range(2, K+1):
x=1
for j in range(2, N+1):
while x<j+1:
if dp[i][x-1]>=dp[i-1][j-x]:
dp[i][j]=1 + max(dp[i-1][j-x], dp[i][x-1])
break
x+=1
return dp[-1][-1]