891. Super Egg Drop

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zjucor/article/details/81635067

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. 1 <= K <= 100
  2. 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 kdp[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]

猜你喜欢

转载自blog.csdn.net/zjucor/article/details/81635067
egg