高楼扔鸡蛋,非常浅显易懂的方式,但是复杂度并不是最低啊
你面前有一栋从 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循环结束后求最小值也可以。