鸡蛋掉落问题算是一道经典的算法题目了,leetcode上面也有收录,是被我收藏了的少数几道题目之一,确实是挺有意思的一道题目,李永乐老师也做过视频讲过这个问题。
刚好今天身体不太舒服,感冒难受的不行,也没啥精力去学一些新的东西,就把老东西拾起来稍微拾掇一下,稍微复习一下好了。
首先,我们来看一下经典算法问题的描述:
对应的leetcode的题目描述为:
这个题目事实上我拿到的第一反应考虑能不能够直接给出最优求解方案,结果就比较呵呵了,后来也是看了一些解答之后发现这是一道动态规划的经典题目,于是至少算法就能够求解了。
显然,如果只有一个鸡蛋,那么有几层楼就必须做几次实验才能够确保找出临界层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个楼层需要多少次实验才能够确保获得临界层。
给出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)量级的。
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次操作最多可以确保分别出多少楼以下的阈值楼层。
给出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当中顺利通过。
综上,这道题我们基本也就说明完毕了。不过,当前的执行效率依然不是最优的,leetcode上面还有一些改进的方案将耗时可以进一步优化,但是思路上是相同的,总体算法复杂度也同样是 O ( N 2 ) O(N^2) O(N2),就是细节实现上不太相同,为了更好地进行说明,我们还是采用了上述这种写法,其解释性更强一点。
如果有读者对更高效的代码感兴趣的话,可以自行去leetcode上面去看一下。