目录
1 扔鸡蛋问题
2 解题分析&反思
3 Code
4 总结
又称鸡蛋掉落问题。题目,如下:
图片来源: https://leetcode-cn.com/problems/super-egg-drop/推荐查看这个解题思路:《丢鸡蛋问题》重制版来袭。
程序里貌似有个小bug,大家注意,我已在这篇帖子下的评论区留言(debug了半天,最后成功debug眼泪掉下来。因为这个bug缕了好几次逻辑,把这个思路缕得差不多了)。
同时,在debug的过程中切实体会了动态规划解题的缺点,当矩阵边界很大时相当耗费时间和空间(K=4, N=5000 会报超时错误)。
其次,递归是有深度限制的,解题能不用递归尽量不用递归。除非非得要求空间复杂度为O(1),不让建notebook,那没办法,不过估计这种要求的题给定参数的范围不会太大。
#
class Solution:
def superEggDrop(self, K, N):
# K 蛋数
# N 总楼层数
dp = [[i if j<=1 or i<=1 else 0 for j in range(K+1) ]
for i in range(N+1)]
# N* K 我这里建的记事本直接初始化了部分结果:楼层和蛋小于等于1时直接用楼层数作为答案。
for i in range(N+1):
for j in range(K+1):
if j<=1 or i <=1:
continue # 一定要跳过啊,要不然白初始化了,下面直接覆盖了。
dp[i][j] = i
for f in range(1, i+1):
dp[i][j] = min(dp[i][j], max(dp[i-f][j], dp[f-1][j-1])+1)
# 转移方程
return dp[N][K]
print(Solution().superEggDrop(4,5000))
分析:
1. 相比“动态规划+递归”,记事本方法可以解决递归深度限制。利用查记事本的原理省略递归。
知识点:递归深度可以设置吗?可以。
python默认的递归深度是很有限的,当递归深度超过这个值的时候,就会引发这样的一个异常。解决的方式是手工设置递归调用深度[参考]:
import sys
sys.getrecursionlimit() # 查询本机的默认参数 我是1000
sys.setrecursionlimit(1000000) # 例如这里设置为一百万
2. 总得来看是三层循环
楼层循环 + 蛋循环 + 蛋数量一定的情况下,小于给定楼层的 各个楼层 确定F 需要扔蛋的次数。
3. 列表初始化
dp = [[i if j<=1 or i<=1 else 0 for j in range(K+1) ]
防止边界错误的小花式,这样不用再额外对i<=1和j<=1的情况赋值了。
4. 空间消耗是个硬伤
在N=5000的情况下直接报超时错误。所以,由此引出第二种主流思路,利用给定蛋数 和扔蛋次数,来确定获取的最高楼层。
class Solution:
def superEggDrop(self, K: int, N: int) -> int:
dp = [[0] * (K+1) for _ in range(N+1)]
t = 0
# dp[t][k] 使用k个鸡蛋,扔t次,能最多确认的楼层
while dp[t][K] < N:
t += 1
for k in range(1, K + 1):
dp[t][k] = dp[t-1][k-1] + 1 + dp[t-1][k] # 在最坏情况下算完了,就不用继续往下算了
return t
网上很多帖子中都说了这种方法的主要思路:
我们这样来思考这个问题。 既然题目要求最少的扔的次数,假设有一个函数 f(k, i),他的功能是求出 k 个鸡蛋,扔 i 次所能检测的最高楼层。[参考]
那么我就有疑问了,凭什么可以通过扔i次检测最高的楼层是否大于给定层数N来获得答案?
于是我输出了一下刚才的记事本中矩阵。
# (K=2, N=7)
[[0, 0, 0],
[1, 1, 1],
[2, 2, 2],
[3, 3, 2],
[4, 4, 3],
[5, 5, 3],
[6, 6, 3],
[7, 7, 4]]
# (K=2, N=10)
[[0, 0, 0],
[1, 1, 1],
[2, 2, 2],
[3, 3, 2],
[4, 4, 3],
[5, 5, 3],
[6, 6, 3],
[7, 7, 4],
[8, 8, 4],
[9, 9, 4],
[10, 10, 4]]
答案的规律是单调递增的,是不是更好理解了。
扔鸡蛋问题的处理过程经历了递归-动态规划-优化-再优化的过程,主流思路2从另一个角度化简问题可以用于很多情景。网友说这已经超过了面试题的难度,有点竞赛题的意思了。
这就是算法之美吧,能化乱麻为井绳,能化暴力为优雅。