线性动态规划:鸡蛋掉落问题(进阶版——带楼层限制的鸡蛋掉落)
题目描述
假设有 \(k\) 个相同的鸡蛋和一栋从第 \(1\) 层到第 \(n\) 层的建筑。现在要确定从哪一层楼扔鸡蛋,鸡蛋会恰好摔碎。
规则:
- 如果鸡蛋从某一层扔下后没有摔碎,那么鸡蛋可以继续使用;
- 如果鸡蛋从某一层扔下后摔碎了,这个鸡蛋就不能再用了;
- 鸡蛋如果在某一层不碎,那么在更低的楼层也不会碎;
- 鸡蛋如果在某一层碎了,那么在更高的楼层也会碎;
- 有一个额外限制:最多只能允许鸡蛋在某个楼层 \(m\)(\(m \le n\))及以下楼层测试,在 \(m+1\) 层及以上楼层禁止扔鸡蛋(即测试楼层上限为 \(m\))。
目标是:在最坏情况下,以最少的测试次数找出鸡蛋恰好会摔碎的临界楼层。
如果鸡蛋在最高测试楼层 \(m\) 都不碎,则临界楼层视为 \(m+1\)(但实际上我们无法在 \(m+1\) 层测试,因此这个结果是“假设”的,实际上我们只能确定临界楼层在 \(m+1\) 或更高,但无法测试更高层,所以问题转化为确定是否在 \(1 \sim m\) 层中摔碎)。
问题分析
这是经典鸡蛋掉落问题(Egg Dropping)的扩展,多了一个测试楼层上限 \(m\)。
经典问题中,可以在 \(1\) 到 \(n\) 任意楼层测试,现在只能在 \(1\) 到 \(m\) 测试。
如果鸡蛋在 \(m\) 层都不碎,我们无法知道它在 \(m+1\) 层是否碎,所以答案可能是“临界楼层是 \(m+1\) 或更高”,但题目要求找出确切的临界楼层,那么我们必须保证在 \(1 \sim m\) 中能确定出来,也就是说,真正的临界楼层必须 \(\le m\),否则无解。
但题目通常会假设临界楼层一定在 \(1 \sim m\) 之间,那么我们的目标就是在 \(1 \sim m\) 之间用最优策略找出临界楼层的最小最坏情况测试次数。
动态规划思路
1. 状态定义
设 \(dp[e][f]\) 表示:
- 有 \(e\) 个鸡蛋
- 允许测试的楼层范围为 \(1\) 到 \(f\)(注意这里的 \(f\) 是当前允许测试的最高楼层,初始时 \(f = m\))
- 在已知临界楼层在 \(1\) 到 \(f\) 之间(如果鸡蛋在 \(f\) 层不碎,则临界楼层就是 \(f+1\) 但不可测,所以我们必须保证鸡蛋在 \(f\) 或以下会碎,否则无解。但题目通常保证临界楼层在 \(1\) 到 \(m\) 之间,所以可行。)
目标:求 \(dp[k][m]\)。
但更常见的状态定义(经典鸡蛋掉落)是:
\(dp[e][f]\) = 在 \(e\) 个鸡蛋、最多测试 \(f\) 层楼时,能保证找到临界楼层的最小最坏情况测试次数。
2. 状态转移
假设我们第一次测试从第 \(x\) 层扔鸡蛋(\(1 \le x \le f\)):
- 如果鸡蛋碎了:
临界楼层在 \(1\) 到 \(x-1\) 之间,鸡蛋数变为 \(e-1\) 个,还需测试 \(x-1\) 层,所以需要 \(dp[e-1][x-1]\) 次。 - 如果鸡蛋没碎:
临界楼层在 \(x+1\) 到 \(f\) 之间,鸡蛋数还是 \(e\) 个,剩下楼层数是 \(f-x\) 层,所以需要 \(dp[e][f-x]\) 次。
最坏情况是取两者的最大值再加 1(本次测试):
\[\text{cost}(x) = 1 + \max(dp[e-1][x-1], dp[e][f-x]) \]
我们要选择最优的 \(x\) 使得这个最坏情况最小:
\[dp[e][f] = \min_{1 \le x \le f} \left( 1 + \max(dp[e-1][x-1], dp[e][f-x]) \right) \]
3. 边界条件
- 如果 \(e = 1\):只能线性一层层试,最坏要试 \(f\) 次,所以 \(dp[1][f] = f\)。
- 如果 \(f = 0\):不需要测试,\(dp[e][0] = 0\)。
- 如果 \(f = 1\):只需要一次测试就知道,\(dp[e][1] = 1\)(因为只有一层)。
- 如果 \(e = 0\):不可能测试,设为无穷大(实际不会出现)。
4. 实现注意
直接按上述递推,复杂度为 \(O(k \cdot m^2)\),当 \(m\) 很大时会很慢。
优化方法:注意到 \(dp[e-1][x-1]\) 随 \(x\) 增加而增加,\(dp[e][f-x]\) 随 \(x\) 增加而减少,所以可以用二分搜索找到两者相等的点附近,使得最大值最小。
但为了讲解清楚,我们先按朴素方法说明。
例子说明
假设 \(k = 2\) 个鸡蛋,\(m = 10\) 层楼。
计算 \(dp[2][10]\):
- 初始:\(dp[1][f] = f\),\(dp[e][1] = 1\)。
- 逐步计算:
- 先算 \(e=2, f=1\):\(dp[2][1] = 1\)。
- \(e=2, f=2\):
尝试 \(x=1\):cost = 1 + max(dp[1][0], dp[2][1]) = 1 + max(0, 1) = 2
尝试 \(x=2\):cost = 1 + max(dp[1][1], dp[2][0]) = 1 + max(1, 0) = 2
取 min = 2 → dp[2][2] = 2。 - 继续算到 \(f=10\):
我们手工算几个:
- dp[1][0..10] = [0,1,2,3,4,5,6,7,8,9,10]
- dp[2][1] = 1
- dp[2][2] = 2
- dp[2][3]:
尝试 x=1: 1+max(dp[1][0], dp[2][2]) = 1+max(0,2)=3
x=2: 1+max(dp[1][1], dp[2][1]) = 1+max(1,1)=2
x=3: 1+max(dp[1][2], dp[2][0]) = 1+max(2,0)=3
min=2 → dp[2][3]=2 - dp[2][4]:
x=2: 1+max(1, dp[2][2]=2) = 3
x=3: 1+max(2, dp[2][1]=1) = 3
x=1 和 x=4 结果更大,所以 min=3 → dp[2][4]=3 - 继续可得到 dp[2][10] = 4。
所以 2 个鸡蛋,允许 10 层测试,最坏情况最少需要 4 次测试。
进阶思考
如果题目要求输出具体的最优策略(第一次从哪层扔),可以在递推时记录选择的 \(x\)。
另外,如果鸡蛋数 \(k\) 很大(超过 \(\log_2 m\)),那么可以用二分法策略,此时最小次数为 \(\lceil \log_2 m \rceil\),但鸡蛋数限制了不能超过 \(m\) 次。
总结
这道题是动态规划中“最优化最坏情况”的经典例子,带楼层限制只是将 \(n\) 替换为 \(m\),递推公式不变。
核心是理解“最坏情况最小化”的决策过程,并掌握 \(dp[e][f]\) 的状态转移方程。