891. Super Egg Drop

87 篇文章 0 订阅
给定一定数量的鸡蛋和楼层,确定找到鸡蛋摔碎楼层的最小尝试次数。问题涉及动态规划、二分搜索和优化算法,目标是将时间复杂度降低到O(KNlogN)。
摘要由CSDN通过智能技术生成

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]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值