# LeetCode困难题赏 - 887.扔鸡蛋

# 题目

假设有KK个鸡蛋和NN层楼,每个蛋的性质完全相同,而且如果某个蛋已经碎了,就没法再次使用。假如存在楼层F,F[0,n]F, F\in[0, n],且鸡蛋从任何高于FF的楼层扔下都会碎掉,但从低于或等于FF的楼层扔下则不会碎。

每次移动,都可以使用一个鸡蛋从某个楼层扔下,如果你想准确地测得FF的值,那么在最坏的情况下,最少需要移动几次?

原题链接:Leetcode 887 (opens new window)

# 题解

刚看到这道题时,很容易一头雾水,不知道题目在说些什么,因为扔鸡蛋的结果我们是无法在做题时实时获得的,比如我们在第xx层楼扔第ii个蛋时,是不知道这个蛋是否会碎,而且对于给定楼层总数和鸡蛋总数,我们知道存在楼层FF会令鸡蛋碎掉,但是我们却并不知道这个数值到底是几。

那么这道题到底在说些什么呢?经过分析可以发现,这道题之所以难理解,是因为题目将真正考察的地方隐藏起来了,题目的核心就在于理解最后一句话:在最坏的情况下,最少移动几次。这句话意味着,我们在扔鸡蛋时是可以采取多种策略的,每种策略都对应一种最坏情况,比如:

  1. 采用策略aa:从第一层逐层向上扔,那么最坏情况是min(K,N,F)min(K, N, F)
  2. 采用策略bb:从顶楼逐层向下扔,那么最坏情况是min(K,N,F)min(K, N, F),即与第一种策略类似;
  3. 采用最优策略f^\hat{f},我们现在可能不知道这个策略具体是什么,但是可以确定移动次数是x=f(K,N,F)x=f(K, N, F)

针对上面列出的几种情况,不难发现,移动次数xx是与K,N,FK, N, F有关的函数,即f(K,N,F)f(K, N, F),那么函数ff就可以代表我们选择的策略,由于具体的策略我们是不知道的,所以将策略函数ff也看作一个变量,那么最终我们要求得的xx表达式可以写作:

x=g(K,N,F,f)x=g(K, N, F, f)

到了这一步,我们才算是真正的理解题意了,我们需要找到一个算法gg,在策略空间fΨf\in\Psi中找到最优策略f^\hat{f},并且针对这种策略f^\hat{f},找到最坏情况F\in\[0,N]对应的xx

绕了这么久,可以发现,我们遍历所有可能的策略,并且在每个策略中,都假设楼层FF总是出现在使当前策略ff移动次数最大的楼层处,题目并没有给出如何遍历楼层,也没有给出FF的值,这两个值都需要我们在算法gg中自行遍历。

对于这个问题,一个自然的思路就是使用动态规划的思想来处理。我们使用状态表dp[K][N]dp[K][N]来表示我们拥有KK个鸡蛋和NN层楼时的最小移动次数,那么考虑初始情况,拥有i[0,K]i\in[0,K]个蛋,j[0,N]j\in[0,N]层楼时:

  1. i=0orj=0i=0 or j=0,毋庸置疑,不需要进行测试,因为没有鸡蛋和楼层,dp[0][1]=dp[1][0]=dp[0][0]=0dp[0][1]=dp[1][0]=dp[0][0]=0
  2. i=1,j[1,N]i=1, j\in[1,N],此时我们只有一个蛋,那么只能使用前面提到的策略aa来试探,那么最坏情况就是F=jF=j,即蛋碎的楼层在最大楼层处,那么我们最少尝试次数就是dp[1][j]=j,j[1,N]dp[1][j]=j, j\in[1,N]
  3. i(1,K],j=1i\in(1,K], j=1,我们有不止一个鸡蛋,但是只有一层楼,那么毫无疑问,只需要测试一次就行了,得到dp[i][1]=1,i(1,K)dp[i][1]=1, i\in(1, K)
  4. i(1,K],j(1,N)i\in(1,K], j\in(1,N),即有不止一个鸡蛋,且不止一层楼,那么我们就需要使用最优策略f^\hat{f}来确定最小移动次数,

写成代码形式:

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

    for i in range(2, N + 1):
        for j in range(2, K + 1):
            dp[i][j] = dp[i][j - 1]
            for k in range(1, i + 1):
                dp[i][j] = min(dp[i][j], 1 + max(dp[k - 1][j - 1], dp[i - k][j]))

    return dp[N][K]

算法复杂度:O(KN2)O(KN^2)

查看带有\LaTeX公式渲染的博客内容:https://blog.simplenaive.cn/#/post/20 (opens new window)