鸡蛋掉落
- https://leetcode-cn.com/problems/super-egg-drop/
- $dp[i][j]$表示$i-1$个鸡蛋,$j$层楼,最少的步数
- $dp[i][j]=1+min{max{dp[i-1][k-1],dp[i][j-k]},1<=k<=j}$
- 状态转移方程所采取的策略是:遍历第一次扔鸡蛋的楼层,然后选取其中需要扔鸡蛋次数最少的方案,而在每某层扔鸡蛋的最少次数是1+$max{dp[i-1][k-1],dp[i][j-k]$
- 优化:
- 可以看到对于固定的$j$,即固定的楼数,当k增大时$dp[i-1][k-1]$增大,$dp[i][j-k]$减小,因此这是一个交叉的单调函数,有最优点,为交叉点,可以用二分法求出交叉点复杂度从$O(n)$变成了$O(lgn)$
- 当$k$固定,$j$增大时,$dp[i-1][k-1]$不变,$dp[i][j-k]$变大,因此交叉点一定是单调的,因此可以用一个变量记录交叉点 ,只需要单增检验就行了
- 反向:$dp[i][j]$表示$i-1$个鸡蛋,$j$次扔鸡蛋,最大可以确定多少层楼
- $dp[i][j]=dp[i][j-1]+dp[i-1][j-1]+1$
- 此转移方程的策略是:站在$dp[i-1][j-1]+1$层扔,如果碎了,变成$dp[i-1][j-1]$,如果没碎,变成$dp[i][j-1]$,不管碎没碎,都可以保证检验$dp[i][j-1]+dp[i-1][j-1]+1$层楼
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class Solution
{
public:
int superEggDrop(int K, int N)
{
int dp[K][N+1];
for (int i = 0; i < K;i++)
{
for (int j = 0; j < N + 1;j++)
{
if (i == 0)
{
dp[i][j] = j;
continue;
}
if(j<=1)
{
dp[i][j] = j;
continue;
}
int lo = 0, hi = j;
int mid = (lo + hi) / 2;
while(lo<hi)
{
if (dp[i][mid] == dp[i - 1][j - mid - 1])
{
lo = mid;
break;
}
if(dp[i][mid]<dp[i-1][j-mid-1])
{
lo = mid + 1;
}
else if (dp[i][mid] > dp[i - 1][j - mid - 1])//一定成立
{
hi = mid;
}
mid = (lo + hi) / 2;
}
dp[i][j] = dp[i][lo] < dp[i - 1][j - lo] ? dp[i][lo] + 1 : dp[i - 1][j - lo]+1;
}
}
return dp[K - 1][N];
}
};
int main(){
Solution x;
std::cout<<x.superEggDrop(2, 6);
}
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int max(int x,int y)
{
return x > y ? x : y;
}
int min (int x,int y)
{
return x < y ? x : y;
}
class Solution
{
public:
int superEggDrop(int K, int N)
{
int dp[N+1];
int dp2[N+1];
for (int i = 0; i <=N;i++)
{
dp2[i]= dp[i] = i;
}
for (int k = 1; k < K;k++)
{
for (int i =2, j = 1; i <=N; i++)
{
while (max(dp[j - 1], dp2[i - j]) > max(dp[j], dp2[i - j - 1])&& j<i)
{
j++;
}
dp[i] = max(dp[j - 1], dp2[i - j])+1;
if(i==N)
for (int m = 0; m <=N;m++)
dp2[m] = dp[m];
}
}
return dp[N];
}
};
class Solution
{
public:
int superEggDrop(int K, int N)
{
int dp[N];
for (int i = 0; i < N;i++)
{
dp[i] = i + 1;
}
for (int i = 1; i < K;i++)
{
int temp1, temp2=dp[0];
for (int j = 1; j <N;j++)
{
temp1 = dp[j];
dp[j] = temp2 + dp[j-1] + 1;
temp2 = temp1;
if(dp[j]>N)
break;
}
}
int i = 0;
while(dp[i]<N)
{
i++;
}
return i+1;
}
};