那一天,人们终于想起被【扔鸡蛋问题】统治的恐惧,直呼不变通的动态规划太辣鸡了

目录

1 扔鸡蛋问题

 2 解题分析&反思

3 Code

4 总结


1 扔鸡蛋问题

又称鸡蛋掉落问题。题目,如下:

图片来源: https://leetcode-cn.com/problems/super-egg-drop/

 2 解题分析&反思

        推荐查看这个解题思路:《丢鸡蛋问题》重制版来袭

       程序里貌似有个小bug,大家注意,我已在这篇帖子下的评论区留言(debug了半天,最后成功debug眼泪掉下来。因为这个bug缕了好几次逻辑,把这个思路缕得差不多了)。

       同时,在debug的过程中切实体会了动态规划解题的缺点,当矩阵边界很大时相当耗费时间和空间(K=4, N=5000 会报超时错误)。

       其次,递归是有深度限制的,解题能不用递归尽量不用递归。除非非得要求空间复杂度为O(1),不让建notebook,那没办法,不过估计这种要求的题给定参数的范围不会太大。

3 Code

  • 主流思路1:动态规划+记事本
# 
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的情况下直接报超时错误。所以,由此引出第二种主流思路,利用给定蛋数 和扔蛋次数,来确定获取的最高楼层。

  • 主流思路2:从扔蛋次数入手
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]]

答案的规律是单调递增的,是不是更好理解了。

4 总结

扔鸡蛋问题的处理过程经历了递归-动态规划-优化-再优化的过程,主流思路2从另一个角度化简问题可以用于很多情景。网友说这已经超过了面试题的难度,有点竞赛题的意思了。

这就是算法之美吧,能化乱麻为井绳,能化暴力为优雅。

 

 

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值