鸡蛋掉落(Egg Dropping)问题的最小尝试次数
字数 2308 2025-12-20 11:34:23

鸡蛋掉落(Egg Dropping)问题的最小尝试次数

题目描述
你手中有 k 枚相同的鸡蛋,并面临一座从第 1 层到第 n 层的高楼。已知存在一个楼层 F0 ≤ 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) 鸡蛋碎了
说明 Fx 层下面(0 ~ x-1 层)。
损失一枚鸡蛋,剩下 i-1 个鸡蛋,剩下可尝试次数 j-1 次。
这能确定的楼层数为:dp[i-1][j-1]

(2) 鸡蛋没碎
说明 Fx 层及以上(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] = jdp[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 方法会更快收敛。
鸡蛋掉落(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 方法会更快收敛。