线性动态规划:鸡蛋掉落问题
字数 1206 2025-11-01 09:19:10
线性动态规划:鸡蛋掉落问题
题目描述
你面前有一栋从1到n共n层楼的建筑,以及k个完全相同的鸡蛋。已知存在一个楼层f(0 <= f <= n),从任何低于或等于f的楼层落下的鸡蛋都不会碎,而从任何高于f的楼层落下的鸡蛋都会碎。你的任务是确定f的值是多少。每次操作你可以取一个鸡蛋从某一楼层x扔下:
- 如果鸡蛋碎了,你就失去这个鸡蛋,且需要测试1到x-1层
- 如果鸡蛋没碎,你可以继续用这个鸡蛋测试x+1到n层
你需要找出保证在最坏情况下测试次数最少的策略,并返回最坏情况下的最小移动次数。
解题过程
1. 问题分析
这是一个经典的动态规划问题,核心矛盾在于:
- 鸡蛋数量多时,可以用二分查找策略减少测试次数
- 鸡蛋数量少时,需要更保守的线性测试策略
- 需要在鸡蛋数量和测试次数之间找到平衡
2. 动态规划状态定义
定义dp[k][n]:表示有k个鸡蛋,需要测试n层楼时,在最坏情况下的最小测试次数。
3. 状态转移方程
当我们从第x层(1 <= x <= n)扔鸡蛋时:
- 如果鸡蛋碎了:剩下k-1个鸡蛋,需要测试1到x-1层,共x-1层 → dp[k-1][x-1]
- 如果鸡蛋没碎:剩下k个鸡蛋,需要测试x+1到n层,共n-x层 → dp[k][n-x]
由于要考虑最坏情况,我们取两者中的最大值,然后加上本次测试:
最坏情况测试次数 = 1 + max(dp[k-1][x-1], dp[k][n-x])
我们需要选择最优的x,使得这个最坏情况最小:
dp[k][n] = min{1 + max(dp[k-1][x-1], dp[k][n-x])},其中x从1到n
4. 边界条件
- 当k=1时:只有一个鸡蛋,只能从低到高逐层测试 → dp[1][n] = n
- 当n=0时:没有楼层需要测试 → dp[k][0] = 0
- 当n=1时:只有一层楼,测试一次即可 → dp[k][1] = 1
5. 算法实现
我们可以使用二维DP数组来求解:
def superEggDrop(k, n):
# dp[k][n] 表示k个鸡蛋n层楼的最小测试次数
dp = [[0] * (n + 1) for _ in range(k + 1)]
# 初始化边界条件
for i in range(1, k + 1):
dp[i][1] = 1 # 只有一层楼
dp[i][0] = 0 # 没有楼层
for j in range(1, n + 1):
dp[1][j] = j # 只有一个鸡蛋
# 填充DP表
for i in range(2, k + 1): # 鸡蛋数量从2到k
for j in range(2, n + 1): # 楼层数量从2到n
# 二分查找最优的x
left, right = 1, j
while left < right:
mid = (left + right + 1) // 2
# 鸡蛋碎了的情况
broken = dp[i-1][mid-1]
# 鸡蛋没碎的情况
not_broken = dp[i][j-mid]
if broken > not_broken:
right = mid - 1
else:
left = mid
dp[i][j] = 1 + max(dp[i-1][left-1], dp[i][j-left])
return dp[k][n]
6. 优化思路
上述解法的时间复杂度是O(k×n×logn)。还可以进一步优化:
- 重新定义状态:dp[k][m]表示k个鸡蛋最多测试m次能确定的最高楼层数
- 状态转移:dp[k][m] = dp[k][m-1] + dp[k-1][m-1] + 1
- 找到最小的m使得dp[k][m] >= n
7. 示例验证
以k=2, n=6为例:
- 从第3层开始测试:
- 如果碎了:用剩下1个鸡蛋测试1-2层,最多2次
- 如果没碎:用2个鸡蛋测试4-6层(3层),最多2次
- 总测试次数:1 + max(2, 2) = 3次
总结
鸡蛋掉落问题通过动态规划巧妙地平衡了鸡蛋数量和测试次数之间的关系。核心思想是通过状态转移方程找到最优的测试策略,确保在最坏情况下也能用最少的测试次数找到临界楼层。