zoukankan      html  css  js  c++  java
  • 887. 鸡蛋掉落

    方法1:动态规划

    最重要的是变换思想,从知道鸡蛋数K,知道层数N,求最少次数M;转化为知道鸡蛋数K,假设最多只能扔M次,求最大能排除的层数。

    创建一个二维列表dp[K][N]来记录最大能排除层数(M<=N,即使一层一层扔,最多也只会扔N次,保证不会溢出)。

    dp[0][:]=0 : 0个鸡蛋,无论扔多少次,只能排除0层数;

    dp[:][0]=0 : 无论多少鸡蛋,扔0次,只能排除0层数。
    某一状态,总共有k个鸡蛋,最多扔m次,要找的那一层为第F层。因为dp记录的是最大能排除的层数,所以我们扔的这一层,是最佳的。也就是,能排除F在楼上的情况:蛋没碎,那么还有k个鸡蛋,可以扔m-1次;能排除F在楼下的情况:蛋碎了,那么还有k-1个鸡蛋,可以扔m-1次;能排除F就在当前楼。

    dp[k][m] = dp[k][m-1]+dp[k-1][m-1]+1
    知道了上面的转移方程,我们可以用两个循环,外循环是m一次一次增加,内循环是k一个一个增加。当某一循环中,扔K个鸡蛋可以排除超过N层了,那么说明,当前的m次就足够了。

    class Solution:
        def superEggDrop(self, K: int, N: int) -> int:
            dp = [[0] * (K + 1) for _ in range(N + 1)]
            for m in range(1, N + 1):
                for k in range(1, K + 1):
                    dp[k][m] = dp[k][m-1] + dp[k - 1][m - 1] + 1
                if dp[k][m] >= N:
                    return m

    方法2.递归解法 

    1.定义状态f(k,n):有k个鸡蛋和n层,f(k,n)代表找到临界楼层需要的最小移动次数
    2.鸡蛋只有碎/不碎两种情况。
    3.将鸡蛋扔在x层,若碎,则在[1...x-1]层找(共x-1层),鸡蛋数-1;
    4.若不碎,则在[x+1...n]层找(共n-x层)
    5.我们需要找到最坏的一种情况,因此将鸡蛋扔在x层时,找到临界楼层需要的最小移动次数为fx(k,n) = max{fx(k-1,x-1),fx(k,n-x)},x∈[1,n]
    6.可知f(k,n)应该为所有情况中的最小值,即f(k,n) = min{f1(k,n),f2(k,n)...fn(k,n)}

    class Solution:
        def superEggDrop(self, k: int, n: int) -> int:
            def parse(k, n):
                if n == 1:  # 如果只有1层,不管有多少蛋只需试1次
                    return 1
                elif n == 0:
                    return 0
                elif k == 1:  # 只有1个鸡蛋,则只能逐层试
                    return n
                elif (k, n) in table:
                    return table[(k, n)]
    
                f = float('inf')  # 定义一个无限大数作为初始条件
                for x in range(1, n + 1):  # 将鸡蛋扔在第x层,从第1层开始
                    fx = 1 + max(parse(k - 1, x - 1), parse(k, n - x))
                    f = min(f, fx)
    
                table[(k, n)] = f
                return f
    
            table = {}  # 记忆被计算过的情况
            return parse(k, n)
    

    时间复杂度:O(kn^2)

    优化:

    • 观察可知fx(k-1,x-1)随着x增加单调递增,fx(k,n-x)随着x增加单调递减,因此可以用二分查找出最坏的情况
      即把找出最小值的代码
    f = float('inf')  # 定义一个无限大数作为初始条件
        for x in range(1, n + 1):  # 将鸡蛋扔在第x层,从第1层开始
            fx = 1 + max(parse(k - 1, x - 1), parse(k, n - x))
            f = min(f, fx)
    

    改为:

     while lp <= rp:  
        mid = lp + (rp-lp) // 2  # 二分法优化
        bcase = parse(k-1, mid-1)  # 蛋碎的情况
        notbcase = parse(k,n-mid)  # 不碎的情况
        # fx = 1 + max(bcase, notbcase)
        if bcase > notbcase:
            rp = mid - 1
            f = min(f, bcase + 1)
        else:
            lp = mid + 1
            f = min(f,notbcase + 1)
    

    时间复杂度:O(knlogn)

     

  • 相关阅读:
    EXTJS 动态改变Gird 列值
    EXTJS动态改变store的proxy的params
    获取Spring容器Bean
    EXTJS 6 必填项加星号*
    Maven打包附加配置文件
    MyEclipse 优化
    Android-SurfaceView生命周期
    Android-PopupWindow
    Android-Sqlite3的使用
    Android-adb的使用
  • 原文地址:https://www.cnblogs.com/USTC-ZCC/p/12684127.html
Copyright © 2011-2022 走看看