区间动态规划例题:鸡蛋掉落问题(K个鸡蛋,N层楼版本)
字数 1553 2025-11-20 04:28:15
区间动态规划例题:鸡蛋掉落问题(K个鸡蛋,N层楼版本)
问题描述
你有 K 个相同的鸡蛋,并可以进入一栋从 1 到 N 层共有 N 层楼的建筑。已知存在一个楼层 F(0 ≤ F ≤ N),从任何低于或等于 F 的楼层扔鸡蛋不会碎,而从任何高于 F 的楼层扔鸡蛋会碎。你的目标是确定 F 的值是多少。每次操作,你可以取一个鸡蛋从某一楼层 X 扔下(1 ≤ X ≤ N)。如果鸡蛋碎了,你就不能再使用这个鸡蛋;如果没碎,你可以继续使用它。你需要找到在最坏情况下,你至少需要扔多少次鸡蛋才能确定 F 的值。
解题过程
我们使用动态规划来解决这个问题。定义状态 dp[k][n] 表示有 k 个鸡蛋和 n 层楼时,在最坏情况下确定 F 所需的最少操作次数。
步骤 1: 状态转移思路
- 如果鸡蛋数 k = 1,我们只能从低到高逐层尝试,最坏情况需要 n 次操作(从 1 楼开始试到 n 楼)。
- 如果楼层数 n = 0,不需要任何操作,
dp[k][0] = 0。 - 对于一般情况(k > 1, n > 0),我们考虑从第 x 层(1 ≤ x ≤ n)扔一次鸡蛋:
- 如果鸡蛋碎了,那么 F 在 x 层以下,剩余问题为
dp[k-1][x-1](鸡蛋数减 1,楼层数为 x-1)。 - 如果鸡蛋没碎,那么 F 在 x 层或以上,剩余问题为
dp[k][n-x](鸡蛋数不变,楼层数为 n-x)。 - 由于要保证最坏情况,我们需要取这两种情况的最大值,并加上本次操作(扔一次鸡蛋)。然后遍历所有可能的 x,选择最小值。
状态转移方程:
dp[k][n] = min_{1≤x≤n} { max(dp[k-1][x-1], dp[k][n-x]) + 1 }
- 如果鸡蛋碎了,那么 F 在 x 层以下,剩余问题为
步骤 2: 初始化
dp[k][0] = 0(0 层楼不需要操作)dp[1][n] = n(只有 1 个鸡蛋时,最坏需试 n 次)- 其他情况可初始化为一个较大值(如无穷大),以便后续更新。
步骤 3: 递推计算
我们按鸡蛋数 k 从 1 到 K,楼层数 n 从 1 到 N 进行双重循环,计算每个 dp[k][n]。
例如,K=2, N=6:
- k=1 时,
dp[1][n] = n - k=2, n=1: 只有 1 层,只需 1 次操作,
dp[2][1] = 1 - k=2, n=2: 从 1 层试,碎了则 F=0,没碎则再试 2 层,最多 2 次。
dp[2][2] = 2 - k=2, n=3: 从 2 层扔,碎了则试 1 层(1 次),没碎则试 3 层(剩余 1 层,1 次),最坏 2 次。
dp[2][3] = 2 - 继续计算更高楼层。
步骤 4: 优化计算
直接遍历 x 会带来 O(K*N^2) 的时间复杂度,当 N 较大时可能超时。我们可以利用 dp[k-1][x-1] 随 x 递增、dp[k][n-x] 随 x 递减的性质,使用二分查找优化 x 的搜索过程:
- 对于固定的 k 和 n,函数
f(x) = max(dp[k-1][x-1], dp[k][n-x])是一个先减后增的函数(或单调),最小值点在两者交点附近。 - 使用二分查找在 x 的范围内快速找到使
dp[k-1][x-1]和dp[k][n-x]最接近的 x,从而减少计算量。
优化后时间复杂度可降为 O(KNlogN)。
步骤 5: 边界情况与结果
- 如果 K=0,无法操作,除非 N=0。
- 如果 N=0,结果直接为 0。
最终答案为dp[K][N]。
总结
这个问题的核心在于将扔鸡蛋的决策转化为对楼层区间的分割,并通过动态规划记录不同鸡蛋和楼层数下的最优解。通过状态转移和优化,我们能够高效求出最坏情况下的最小尝试次数。