题目
给你 k 枚相同的鸡蛋,并可以使用一栋从第 1 层到第 n 层共有 n 层楼的建筑。
已知存在楼层 f ,满足 0 <= f <= n ,任何从 高于 f 的楼层落下的鸡蛋都会碎,从 f 楼层或比它低的楼层落下的鸡蛋都不会破。
每次操作,你可以取一枚没有碎的鸡蛋并把它从任一楼层 x 扔下(满足 1 <= x <= n)。如果鸡蛋碎了,你就不能再次使用它。如果某枚鸡蛋扔下后没有摔碎,则可以在之后的操作中 重复使用 这枚鸡蛋。
请你计算并返回要确定 f 确切的值 的 最小操作次数 是多少?
示例 1:
输入:k = 1, n = 2
输出:2
解释:
鸡蛋从 1 楼掉落。如果它碎了,肯定能得出 f = 0 。
否则,鸡蛋从 2 楼掉落。如果它碎了,肯定能得出 f = 1 。
如果它没碎,那么肯定能得出 f = 2 。
因此,在最坏的情况下我们需要移动 2 次以确定 f 是多少。
示例 2:
输入:k = 2, n = 6
输出:3
示例 3:
输入:k = 3, n = 14
输出:4
dp 二分思路
dp [ k ][ n ] k表示鸡蛋数,n表示搜索楼层区间
class Solution {
public:
int **dp;
int superEggDrop(int k, int n) {
dp=new int*[k+1];
for(int i=0;i<k+1;i++)
{
dp[i]=new int[n+1];
memset(dp[i],0,(n+1)*sizeof(int));
}
return EggDP(k,n);
}
int EggDP(int k,int n) //k表示鸡蛋数,n表示搜索楼层区间
{
if(k==1) //鸡蛋数为1时只能线性扫描楼层
return n;
if(n==0)
return 0;
if(dp[k][n]!=0)
return dp[k][n];
int res=INT_MAX;
int left=1;
int right=n;
while(left<=right)
{
int mid=(left+right)/2;
int broken=EggDP(k-1,mid-1); //碎了
int not_broken=EggDP(k,n-mid); //没碎
if(broken >not_broken ) //broken 最坏情况
{
right=mid-1;
res=min(res,broken+1); //比较的两种情况取最坏情况
}
else
{
left=mid+1;
res=min(res,not_broken+1);
}
}
dp[k][n]=res;
return res;
}
};
高效dp
class Solution {
public:
int **dp;
int superEggDrop(int k, int n) {
dp=new int*[k+1];
for(int i=0;i<k+1;i++)
{
dp[i]=new int[n+1];
memset(dp[i],0,(n+1)*sizeof(int));
}
int m=0;
//dp[k][m],表示k个鸡蛋,最多允许的扔鸡蛋次数m,能确定的最高楼层数
while(dp[k][m]<n)
{
m++;
for(int i=1;i<=k;i++)
{
dp[i][m]=dp[i][m-1]+dp[i-1][m-1]+1;
//1.dp[i][m-1]代表鸡蛋没碎,次数-1,往楼上找的层数
//2.dp[i-1][m-1]代表鸡蛋碎了,次数-1,往楼下找的层数
//以上两个+1就是dp[i][m]找到的总层数
}
}
return m;
}
};
终极版本,一维dp
class Solution {
public:
int superEggDrop(int k, int n) {
int *dp=new int[k+1];
memset(dp,0,(k+1)*sizeof(int));
int m=0;
while(dp[k]<n)
{
m++;
int pre=0;
for(int i=1;i<=k;i++)
{
int temp=dp[i];
dp[i]=dp[i]+pre+1;
pre=temp;
}
}
return m;
}
};