高楼扔鸡蛋,非常浅显易懂的方式,但是复杂度并不是最低啊

你面前有一栋从 1 到 N 共 N 层的楼,然后给你 K 个鸡蛋( K 至少为 1)。现在确定这栋楼存在楼层 0 <= F <= N ,在这层楼将鸡蛋扔下去,鸡蛋恰好没摔碎(大于 F 的楼层都会碎,低于 F 的楼层都不会碎)。现在问你,最坏情况下,你最少要扔几次鸡蛋,才能确定这个楼层F 呢?

思路想法就是动态规划,定义一个二维dp数组dp[K][N]。代表有K个鸡蛋,N层楼时最少需要尝试几次就能找到鸡蛋摔不碎的楼层

那么我有了一个等式。我在第 i 层楼扔第一个鸡蛋,这个鸡蛋要么碎了,要么不碎,碎了的话问题变成有K-1个鸡蛋N层楼。这个已经解决了的呀。没碎,那么我变成有K个鸡蛋N-i 层楼。这个也解决了。注意动态规划的时候我们认为之前的状态是已知的。

dp[K][N] = dp[K-1][N] +1 (碎了)
dp[K][N] = dp[K][N-i] + 1(没碎)

怎么判断他碎没碎呀,因为求得是最坏情况,那么我们选上面两个等式中较大的值(较大说明需要次数更多,可不就是最坏吗)

那么就有了,我们从第 i 层楼扔第一个鸡蛋,对应的最坏情况要的次数是 max(dp[K-1][N] , dp[K][N-i] )+1

那么N层楼,我们第一个鸡蛋有 N 种扔法,有N个 max(dp[K-1][N] , dp[K][N-i] )+1。N种扔法里面最小的赋值给 dp[K][N] 就是我们题目要求的最少次数

基本就解决了,最后就是初始化DP,比如说只有一个鸡蛋,那么最坏肯定是当前一个鸡蛋对应的N,一层楼都没有测试0次,只有一层,测试1次。

代码大致如下

def superEggDrop(K: int, N: int):
    memo = dict()      # 避免重复计算备忘录
    def dp(K, N) -> int:
        if K == 1: return N
        if N == 1: return 1
        if N == 0: return 0
        if (K, N) in memo:   #存在了的数据就不要重复计算了
            return memo[(K, N)]
        res = float('INF')
        for i in range(1, N + 1):   # 第一个鸡蛋有N种扔法
            res = min(res,max(dp(K, N - i),dp(K - 1, i - 1)) + 1)
        memo[(K, N)] = res   # 记备忘录
        return res
    return dp(K, N)

其实这段代码 res = min(res,max(dp(K, N - i),dp(K - 1, i - 1)) + 1) 你也可以先用一个数组把N个 max(dp[K-1][N] , dp[K][N-i] )+1 存储起来,for循环结束后求最小值也可以。

posted on 2021-06-10 16:49  雾恋过往  阅读(356)  评论(0编辑  收藏  举报

Live2D