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)

 

原文地址:https://www.cnblogs.com/USTC-ZCC/p/12684127.html