LeetCode 887. 鸡蛋掉落
你将获得 K 个鸡蛋,并可以使用一栋从 1 到 N 共有 N 层楼的建筑。每个蛋的功能都是一样的,如果一个蛋碎了,你就不能再把它掉下去。
你知道存在楼层 F,满足 0 <= F <= N 任何从高于 F 的楼层落下的鸡蛋都会碎,从 F 楼层或比它低的楼层落下的鸡蛋都不会破。每次移动,你可以取一个鸡蛋(如果你有完整的鸡蛋)并把它从任一楼层 X 扔下(满足 1 <= X <= N)。
你的目标是确切地知道 F 的值是多少。无论 F 的初始值如何,你确定 F 的值的最小移动次数是多少?
数据范围:1 <= K <= 100 1 <= N <= 10000
示例 1:
1 | 输入:K = 1, N = 2 |
示例 2:
1 | 输入:K = 2, N = 6 |
示例 3:
1 | 输入:K = 3, N = 14 |
题解:
考虑动态规划,我们用 dp[K][N] 表示有 K 个鸡蛋、N 层楼的状态下所需要的最少步数。当我们把一个鸡蛋从第 X 层楼扔下后,会有两种可能的结果:
- 鸡蛋没碎,那么我们还是有
K个鸡蛋,但答案只可能在第X层以上了,也就是说楼层数变为了N-X,接下来需要的最少步数为dp[K][N-X] - 鸡蛋碎了,那么我们只剩下
K-1个鸡蛋了,答案被锁定在下面的X-1层楼种,故接下来需要的最少步数为dp[K-1][X-1]
因为我们需要考虑的是在最坏情况下所需的步数,所以取二者的最大值作为从第 X 层楼扔下后需要的最少步数。然后,为了让最坏情况下需要的步数最少,我们需要确定究竟哪一个 X 的取值能够使得这个最大值最小。综上,我们可以列出状态转移方程如下:
有了转移方程,我们就可以求解每个状态的 dp 值了,代码如下:
1 | int superEggDrop(int K, int N) |
考虑一下时间复杂度,一共 种状态,每种状态需要枚举 X,耗时为 ,因此时间复杂度为 ,显然会超时,因此我们需要优化时间复杂度。
状态数不好优化,那么我们就优化求解每种状态即确定 X 取值的时间复杂度。我们发现,dp[K][N] 是关于 N 的增函数,于是有:
dp[K][N-X]随X增加单调递减dp[K-1][X-1]随X增加单调递增
而我们要找的是使得 dp[K][N-X] 和 dp[K-1][X-1] 的最大值最小的 X 的取值,显然,上述两个函数相交即 dp[K][N-X] = dp[K-1][X-1] 时,它们的最大值最小,交点对应的 X 的取值就是我们要找的 X。
通过以上对单调性的分析,我们就可以通过二分查找找到那个使得 dp[K][N-X] <= dp[K-1][X-1] 的 X 的最小取值。这样,我们就把时间复杂度优化到了 ,可成功通过此题。代码如下:
1 | int superEggDrop(int K, int N) |