经典算法:鸡蛋掉落问题

0. 引言

鸡蛋掉落问题算是一道经典的算法题目了,leetcode上面也有收录,是被我收藏了的少数几道题目之一,确实是挺有意思的一道题目,李永乐老师也做过视频讲过这个问题。

刚好今天身体不太舒服,感冒难受的不行,也没啥精力去学一些新的东西,就把老东西拾起来稍微拾掇一下,稍微复习一下好了。

1. 问题简介

首先,我们来看一下经典算法问题的描述:

  • 你手上有K个鸡蛋,然后有一幢N层高的楼,他有一个临界楼层F,当鸡蛋从F层或更低的楼层中掉落的话,他都不会碎裂;反之,当鸡蛋从高于F层的楼上掉落时,鸡蛋就会碎裂,求问最小需要几次实验才能够确保找出临界层F。

对应的leetcode的题目描述为:

2. 算法思路

这个题目事实上我拿到的第一反应考虑能不能够直接给出最优求解方案,结果就比较呵呵了,后来也是看了一些解答之后发现这是一道动态规划的经典题目,于是至少算法就能够求解了。

显然,如果只有一个鸡蛋,那么有几层楼就必须做几次实验才能够确保找出临界层F;而如果只有一层楼,那么无论手上有多少鸡蛋,所需要进行的实验次数都是一次。

那么,我们就可以很快速的给出递推公式如下:

dp[k][n] = min(1+max(dp[k-1][i-1], dp[k][n-i]) for i in range(1, n+1))

即是说,考察每一次操作从第i层掉落的情况,如果没有碎,那么只需要考察楼上的第n-i层即可;反之,如果碎了,那么只要考察在k-1个鸡蛋的情况下对i-1个楼层需要多少次实验才能够确保获得临界层。

3. 代码实现

给出python的代码实现如下:

class Solution:
    def superEggDrop(self, K: int, N: int) -> int:
        dp = [[0 for i in range(N+1)] for _ in range(K+1)]
        for k in range(1, K+1):
            for n in range(1, N+1):
                if k == 1:
                    dp[k][n] = n
                elif n == 1:
                    dp[k][n] = 1
                else:
                    dp[k][n] = min(1+max(dp[k-1][i-1], dp[k][n-i]) for i in range(1, n+1))
        return dp[K][N]

提交代码之后测试了几个例子,均是正确的,但是在leetcode中进行代码评测的时候出现了超时错误,毕竟上述代码的时间复杂度是 O ( N 3 ) O(N^3) O(N3)量级的。

4. 算法优化

leetcode当中给出了另一种更为优雅的代码实现。

首先,他的思路并不像我们前面给出的那样,是明确地去计算对于每一个明确的K、N情况下的具体答案,而是求解当有K个蛋时,做n次实验,最多可以确保确定多少层楼以下的阈值楼层

我们同样可以给出递推公式如下:

dp[k][n] = dp[k-1][n-1] + 1 + dp[k][n-1]

其中,k和之前的定义一致,表示有k个蛋,n表示最多经过n次操作,dp[k][n]表示当有k个蛋时,经过n次操作最多可以确保分别出多少楼以下的阈值楼层。

5. 代码实现

给出python代码实现如下:

class Solution:
    def superEggDrop(self, K: int, N: int) -> int:
        dp = [[0 for _ in range(N+1)] for _ in range(K+1)]

        for n in range(1, N+1):
            for k in range(1, K+1):
                if k == 1:
                    dp[k][n] = n
                else:
                    dp[k][n] = dp[k-1][n-1] + 1 + dp[k][n-1]
                if dp[k][n] >= N:
                    return n
        return -1

代码复杂度从之前的 O ( N 3 ) O(N^3) O(N3)退化到 O ( N 2 ) O(N^2) O(N2),提交代码之后在leetcode当中顺利通过。

6. 总结

综上,这道题我们基本也就说明完毕了。不过,当前的执行效率依然不是最优的,leetcode上面还有一些改进的方案将耗时可以进一步优化,但是思路上是相同的,总体算法复杂度也同样是 O ( N 2 ) O(N^2) O(N2),就是细节实现上不太相同,为了更好地进行说明,我们还是采用了上述这种写法,其解释性更强一点。

如果有读者对更高效的代码感兴趣的话,可以自行去leetcode上面去看一下。

  • 6
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
鸡蛋掉落实验是一种经典算法问题,也被称为“鸡蛋楼层问题”。问题描述如下: 假设你有N个鸡蛋和一栋具有K层楼的建筑物。假设如果鸡蛋从第i层及以上的楼层落下,就会碎裂,否则它不会碎。你的任务是确定F,其中0≤F≤K,使得在最坏情况下,你必须扔F次鸡蛋才能确定F的值。 这个问题可以用动态规划或二分查找来解决。下面介绍一下二分查找的解法。 假设我们第一次在第x层扔鸡蛋,如果鸡蛋碎了,那么我们需要在前x-1层楼中继续测试。如果鸡蛋没有碎,我们需要在后K-x层楼中继续测试。因此,我们可以将问题转化为在前x-1层楼中测试和在后K-x层楼中测试的两个子问题,以此类推。我们可以使用二分查找来确定最优的x值,使得这两个子问题的最大尝试次数最小。 具体地,我们可以用一个二维数组dp[i][j]表示i个鸡蛋,j层楼的情况下最坏情况下需要扔多少次鸡蛋才能确定F的值。假设我们在第x层扔了一个鸡蛋,如果鸡蛋碎了,那么我们需要在前x-1层楼中继续测试,因此需要在dp[i-1][x-1]中继续测试;如果鸡蛋没有碎,我们需要在后K-x层楼中继续测试,因此需要在dp[i][j-x]中继续测试。因此,我们可以得到递推公式: dp[i][j] = min(max(dp[i-1][x-1], dp[i][j-x])+1) (1 <= x <= j) 其中max(dp[i-1][x-1], dp[i][j-x])表示在第x层扔鸡蛋后两个子问题中最坏情况下需要的最大尝试次数,+1表示扔鸡蛋的一次操作。 最后,我们可以通过二分查找来确定最优的x值,使得dp[i][j]的值最小。具体地,我们可以从1到K层依次枚举x值,然后计算dp[i][j],并更新最优解。这样的时间复杂度为O(KlogK)。 参考代码如下:

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值