【理解题目】最坏情况下,至少扔几次鸡蛋,找到鸡蛋恰好不会碎的楼层
最坏情况:不管用什么策略,鸡蛋破碎一定发生在穷举尽搜索空间的时候。
比如总共7层楼,用线性搜索,从第一层开始往上,搜索到第七层鸡蛋碎,这就是最坏情况
用二分搜索,鸡蛋在第七层破碎,需要扔第一次(1+7)/2=4,(5+7)/2=6, 7,三次才破碎,这就是二分的最坏情况
至少几次:如果不限制鸡蛋的个数,那么二分就是最少的搜索次数(即使是最坏情况下最多是logn次)
【先考虑只有两个鸡蛋的情况,在第i层扔第一个鸡蛋】
(1)如果碎了,那么就要从第一层开始线性搜索,直到鸡蛋破碎,最坏情况下,扔i-1次。为什么碎了,就要线性搜索,因为只有两个鸡蛋,如果这个碎了,那么只剩下一个鸡蛋了,必须保证在找到临街楼层之前不能碎,所以只能线性搜索了
(2)如果鸡蛋没碎,那么可以对(i+1,顶层)进行二分搜索,直到鸡蛋碎了,再进行线性搜索
N:楼层总数
i:在第i层扔鸡蛋
f(x):x层楼,最坏情况下,至少扔几次
所以在第i层扔,最坏情况下要扔的总次数
=在i层扔的这一次+max( 碎了的情况,没碎的情况)=1+max(i-1,f(100-i))
然后只要遍历i,找最小的总次数
最坏情况取决于: 这次扔碎了还是没碎,分别要再扔几次(第i层碎了最多还要扔几次,第i层没碎最多还要扔几次)的最多的那种,就是第i层扔的最坏情况
至少要扔几次取决于:扔的策略,从第几层开始扔,就是i的选择,最后选择总次数最少的i
N层楼,K的鸡蛋
状态转移方程
状态:n层楼,k个鸡蛋
选择:从i层扔
转移到的状态:
碎了:1+dp(i-1,k-1)
没碎:1+dp(n-i,k)
int dp(int N,int K)
{//返回最坏情况下,至少要扔鸡蛋的次数
int res;
for(int i=1;i<=N;i++)//遍历选择,从第i层扔
{
res=min(res,max(dp(i-1,k-1),dp(N-i,k))+1)
碎了 没碎
}
return res;
}
其中min是求扔鸡蛋总次数最小的i
max是求碎了和没碎两种情况中扔鸡蛋总次数最大的那种需要的次数
base case
对于n,n=0的时候,扔0次
对于k,k=1的时候,只能线性扫描,扔n次
添加备忘录递归
int memo[k][n];//备忘录
memset(memo,0,sizeof(memo));
int dp(int k,int n)
{
if(k==1) return n;
if(n==0) return 0;
if(!memo[k][n]) return memo[k][n];
int res;
for(int i=1;i<=n;i++)
{
res=min(res,max(dp(k-1,i-1),dp(k,n-i)+1);
}
memo[k][n]=res;
return res;
}
时间复杂度
动态规划算法的时间复杂度就是子问题个数 × 函数本身的复杂度。
子问题的个数:
O
(
K
N
)
O(KN)
O(KN)
本身的复杂度:
O
(
N
)
O(N)
O(N)
总时间复杂度:
O
(
K
N
2
)
O(KN^2)
O(KN2)
二分查找
对于函数dp(k,n)返回值一定是随着n的增大递增的,那么
当i增大的时候,碎了:dp(k-1,i-1)递增
没碎:dp(k,n-i)递减
而求所有i里面 max(dp(k-1,i-1),dp(k,n-i))的最小值,其实就是求这两个函数的交点即
dp(k-1,i-1)==dp(k,n-i)的时候i的取值
可以用二分查找:
令i=mid=low+(high-low)/2,
分别计算碎了和没碎 broken=dp(k-1,mid-1)+,unbroken=dp(k,n-mid)
如果碎了>没碎:
更新res,此时至少扔几次的结果是,之前计算的至少,和现在最坏情况的至少中小的那个:res=min(res,broken+1)
同时说明交点在mid的左边区间:high=mid-1
如果没碎>碎了:
res=min(res,unbroken+1)
同时说明交点在mid的右边区间:low=mid+1
int dp(int k,int n)
{
if(k==1) return n;
if(n==0) return 0;
if(!memo[k][n]) return memo[k][n];
int res;
int low=1,high=n;
while(low<=high)
{
int mid=low+(high-low)/2;
int broken=dp(k-1,mid-1);
int unbroken=dp(k,n-mid);
if(broken>unbroken)
{
res=min(res,broken);
high=mid-1;
}
if(unbroken>broken)
{
res=min(res,unbroken);
low=mid+1;
}
memo[k][n]=res;
}
return res;
}