给你 k 枚相同的鸡蛋,并可以使用一栋从第 1 层到第 n 层共有 n 层楼的建筑。
已知存在楼层 f ,满足 0 <= f <= n ,任何从 高于 f 的楼层落下的鸡蛋都会碎,从 f 楼层或比它低的楼层落下的鸡蛋都不会破。
每次操作,你可以取一枚没有碎的鸡蛋并把它从任一楼层 x 扔下(满足 1 <= x <= n)。如果鸡蛋碎了,你就不能再次使用它。如果某枚鸡蛋扔下后没有摔碎,则可以在之后的操作中 重复使用 这枚鸡蛋。
请你计算并返回要确定 f 确切的值 的 最小操作次数 是多少?
c
#define MAX(x,y) ((x)>(y)?(x):(y))
#define MIN(x,y) ((x)<(y)?(x):(y))
int superEggDrop(int k, int n) {
// dp[i][j] i个鸡蛋j层楼的最小操作次数
int dp[k+1][n+1];
for(int i=0; i<k+1; i++) {
for(int j=0; j<n+1; j++) {
dp[i][j] = INT_MAX;
}
}
for(int j = 0; j<n+1; j++) {
// 1个鸡蛋
dp[1][j] = j;
// 0个鸡蛋
dp[0][j] = 0;
}
for(int i=1; i<k+1; i++) {
// 1层楼
dp[i][1] = 1;
// 0层楼
dp[i][0] = 0;
}
for(int i = 2; i<k+1; i++) {
for(int j=2; j<n+1; j++) {
// 直接遍历选择超时间限制
// 二分搜索优化:找到最大的选择
int left = 1;
int right = j;
int mid;
int lvalue;
int rvalue;
while(left+1 < right) {
// 鸡蛋放在mid层
mid = left+(right-left)/2;
// 碎了
lvalue = dp[i-1][mid-1];
// 没碎
rvalue = dp[i][j-mid];
if(lvalue > rvalue) {
right = mid;
}
else {
// 右边界
left = mid;
}
}
// dp[i][j-left] 左边界选这种方式更大
// dp[i-1][right-1] 右边界选这种方式更大
dp[i][j] = MIN(dp[i][j],MIN(dp[i][j-left],dp[i-1][right-1])+1);
}
}
// for(int i=0; i<k+1; i++) {
// for(int j=0; j<n+1; j++) {
// printf("%d ", dp[i][j]);
// }
// printf("\n");
// }
return dp[k][n];
}