【动态规划】高楼扔鸡蛋问题
高楼扔鸡蛋
这是一个比较经典的动态规划问题,最先来自谷歌的面试题。
题目
方法一:动态规划
分析
我们假设
边界条件
当楼层数为零时,查找次数为
状态转移
我们考虑一般情况,对于第
当我们从第
-
若鸡蛋没有碎
我们可以继续用
枚鸡蛋,在上方的楼层,即 ,共 层楼中,继续寻找 ,因此,所需要查找的次数为 ; -
若鸡蛋碎了
这时,鸡蛋的数量少了一枚,因此,我们就需要用剩余的
枚鸡蛋,在下方的楼层,即 ,共 层楼中,继续寻找 ,因此所需要查找的次数为 。
这里,我们可以看到,通过状态转移,明显将问题的规模缩小了,由于题目是要求在最坏的情况下,扔鸡蛋的最少次数,所以,最坏的情况下,在第
因此,在最坏的情况下,最少的操作次数为:
代码实现
- 自顶向下的递归实现
class Solution: def superEggDrop(self, k: int, n: int) -> int: memory = dict() return self.drop_egg(k, n, memory) def drop_egg(self, k: int, n: int, memory) -> int: if n == 0: return 0 if k == 1: return n if (k, n) in memory: return memory.get((k, n)) result = float("INF") for i in range(1, n + 1): g1 = self.drop_egg(k, n - i, memory) g2 = self.drop_egg(k - 1, i - 1, memory) result = min(result, max(g1, g2) + 1) memory[(k, n)] = result return result
- 自底向上的迭代实现
class Solution: def superEggDrop(self, k: int, n: int) -> int: return self.drop_egg(k, n) def drop_egg(self, eggs: int, floor: int) -> int: dp = [[float("INF") for _ in range(floor + 1)] for _ in range(eggs + 1)] # 鸡蛋数为1时 for n in range(1, floor + 1): dp[1][n] = n # 楼层数为0 for i in range(0, eggs + 1): dp[i][0] = 0 # 楼层数为1 dp[0][1] = 0 for i in range(1, eggs + 1): dp[i][1] = 1 for k in range(2, eggs + 1): for n in range(2, floor + 1): for i in range(1, n + 1): g1 = dp[k][n - i] g2 = dp[k - 1][i - 1] dp[k][n] = min(dp[k][n], max(g1, g2) + 1) return int(dp[eggs][floor])
复杂度
复杂度分析:
- 时间复杂度:
; - 空间复杂度:
。
注:这种方法,直接枚举所有的状态,在力扣上提交的时候,会超时,因此,我们还需要对上述算法做进一步优化。
方法二:动态规划 + 二分搜索
分析
这里,我们定义一个函数
结合前面的分析,我们可以看出,当
是一个随 增加的单调递减函数; 是一个随 增加的单调递增函数;
对于函数
因此,我们可以将函数
那么,状态转移方程,就可以写成:
那么,我们只需要将优化的重点,放在求解
注意:
- 函数
和 函数 并非严格递增或者递减; - 我们可以通过数学方式证明,使得函数
和 函数 在区间 相等的点有两个,分别为 和 ,且 和 相差 ,我们并不需要关心具体值是多少,我们只需要记录它们所对应的函数值即可。
代码实现
- 自顶向下的递归实现
from typing import Dict class Solution: def superEggDrop(self, k: int, n: int) -> int: memory = dict() return self.drop_egg(k, n, memory) def drop_egg(self, k: int, n: int, memory: Dict) -> int: if n == 0: return 0 if k == 1: return n if (k, n) in memory: return memory.get((k, n)) result = float("INF") left, right = 1, n while left <= right: mid = left + (right - left) // 2 g1 = self.drop_egg(k, n - mid, memory) g2 = self.drop_egg(k - 1, mid - 1, memory) result = min(result, max(g1, g2) + 1) if g1 < g2: right = mid - 1 elif g1 > g2: left = mid + 1 else: break memory[(k, n)] = result return result
- 自底向上的迭代实现
class Solution: def superEggDrop(self, k: int, n: int) -> int: return self.drop_egg(k, n) def drop_egg(self, eggs: int, floor: int) -> int: dp = [[float("INF") for _ in range(floor + 1)] for _ in range(eggs + 1)] # 鸡蛋数为1时 for n in range(1, floor + 1): dp[1][n] = n # 楼层数为0 for i in range(0, eggs + 1): dp[i][0] = 0 # 楼层数为1 dp[0][1] = 0 for i in range(1, eggs + 1): dp[i][1] = 1 for k in range(2, eggs + 1): for n in range(2, floor + 1): # 使用二分搜索代替迭代搜索 result = float("INF") left, right = 1, n while left <= right: mid = left + (right - left) // 2 g1 = dp[k][n - mid] g2 = dp[k - 1][mid - 1] result = min(result, max(g1, g2) + 1) if g1 < g2: right = mid - 1 elif g1 > g2: left = mid + 1 else: break dp[k][n] = result return int(dp[eggs][floor])
复杂度
复杂度分析:
- 时间复杂度:
; - 空间复杂度:
。
本文作者:LARRY1024
本文链接:https://www.cnblogs.com/larry1024/p/17057913.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步