鸡蛋掉落(Egg Dropping)问题的最小尝试次数
题目描述
你手中有 k 枚相同的鸡蛋,并面临一座从第 1 层到第 n 层的高楼。已知存在一个楼层 F(0 ≤ F ≤ n),从低于或等于 F 的楼层扔下鸡蛋,鸡蛋不会破碎;从高于 F 的楼层扔下鸡蛋,鸡蛋会破碎。
每次操作,你可以选择一层楼将一枚鸡蛋扔下去:
- 如果鸡蛋没碎,你可以继续使用这枚鸡蛋(鸡蛋的耐用性不变);
- 如果鸡蛋碎了,你就失去了这枚鸡蛋。
你的目标是无论 F 是多少,都要通过实验确定 F 的确切值。你需要计算的是:在最坏情况下,至少要扔多少次鸡蛋,才能保证找到 F。
简单来说,就是求最坏情况下的最小尝试次数。
- 输入:
k个鸡蛋,n层楼。 - 输出:最小尝试次数。
解题过程
这是一个经典的动态规划问题,我们需要在“最坏情况”和“最少尝试”之间找到平衡。
1. 状态定义
定义 dp[i][j] 表示:有 i 枚鸡蛋,最多允许尝试 j 次,在最坏情况下能确定 F 的最大楼层数。
为什么这么定义?
- 直接问“i 个鸡蛋,n 层楼,最少尝试次数”也可以,但转移时会更复杂。
- 而“i 个鸡蛋,j 次尝试,能解决的最大楼层数”更容易用子问题推导。
最终答案是:最小的j,使得dp[k][j] ≥ n。
2. 基础情况
- 如果鸡蛋只有 1 枚(
i = 1):一次只能从第 1 层开始逐层往上试,否则如果碎了就没鸡蛋了,无法继续。所以dp[1][j] = j(尝试 j 次最多能确定 j 层)。 - 如果次数只有 1 次(
j = 1):只能扔一次,如果碎了就知道 F=0,没碎就知道 F ≥ 1,但无法确定是 1 还是更高,所以最多确定 1 层。
即dp[i][1] = 1(i ≥ 1)。 - 没有鸡蛋(
i = 0)时无法测试,dp[0][j] = 0。
3. 状态转移
假设现在有 i 个鸡蛋,允许 j 次尝试。我们从某一层 x 扔一次鸡蛋:
(1) 鸡蛋碎了
说明 F 在 x 层下面(0 ~ x-1 层)。
损失一枚鸡蛋,剩下 i-1 个鸡蛋,剩下可尝试次数 j-1 次。
这能确定的楼层数为:dp[i-1][j-1]。
(2) 鸡蛋没碎
说明 F 在 x 层及以上(x ~ 最高层)。
鸡蛋数量不变(i 个),剩下可尝试次数 j-1 次。
这能确定的楼层数为:dp[i][j-1]。
为什么是“以上”的部分也能确定?
因为我们可以继续在上面的楼层中继续尝试,而我们已经知道 x 层是安全的,所以上面的楼层相当于一个新的子问题,但总尝试次数减少 1。
(3) 合并
这一次尝试(在楼层 x)让我们能确定的总楼层数为:
- 下面的部分:
dp[i-1][j-1]层(确定楼层区间下界)。 - 上面的部分:
dp[i][j-1]层(确定楼层区间上界)。 - 加上
x本身这一层,但注意,x层包含在上面的部分吗?不,更严谨地看:
如果碎了,我们要确定的是下面的dp[i-1][j-1]层(从 1 到该数值)。
如果没碎,我们要确定的是上面的dp[i][j-1]层(从 x+1 往上数这么多层)。
而 x 本身是这次扔的层,无论碎没碎,结果都已经通过这次尝试得到信息。
实际上,更简单的理解是:
dp[i][j] = dp[i-1][j-1] + 1 + dp[i][j-1]
- 加的这个 1 就是当前测试的楼层
x。 - 为什么是相加?因为如果没碎,我们就在上面的楼层中继续测试,能再覆盖
dp[i][j-1]层;
如果碎了,我们就在下面的楼层中测试,能覆盖dp[i-1][j-1]层。
最坏情况下,我们取两者之和,就能保证无论结果如何,总的楼层覆盖范围是这两个部分加当前层。
4. 递推填表
我们按 j 从小到大的顺序计算 dp[i][j],直到 dp[k][j] ≥ n。
边界:dp[1][j] = j,dp[i][1] = 1。
例子:k=2, n=6
初始化:
j=1: dp[2][1] = 1, dp[1][1] = 1
j=2:
dp[2][2] = dp[1][1] + 1 + dp[2][1] = 1 + 1 + 1 = 3
dp[1][2] = 2
j=3:
dp[2][3] = dp[1][2] + 1 + dp[2][2] = 2 + 1 + 3 = 6
此时 dp[2][3] = 6 ≥ 6,所以最少需要 3 次。
5. 答案
答案就是最小的 j 使得 dp[k][j] ≥ n。
时间复杂度
状态数 O(k * m),m 是答案尝试次数,m ≤ n 且通常远小于 n。
在 k 较大时,m ≈ O(log n)(当 k ≥ log n 时可用二分策略),但动态规划方法在 k 较小时是常用解法。
空间优化
因为 dp[i][j] 只依赖于 dp[i-1][j-1] 和 dp[i][j-1],可以用一维数组(按鸡蛋维度倒序更新)优化空间到 O(k)。
6. 边界和细节
- 当 k=0 时无法测试,但题目一般 k ≥ 1。
- 当 n=0 时无需测试,答案为 0。
- 当 k 很大(≥ log₂(n+1))时,可以用二分查找,尝试次数是 ceil(log₂(n+1)),此时 dp 方法会更快收敛。