高楼扔鸡蛋-算法小抄笔记

labuladong高楼扔鸡蛋问题

【理解题目】最坏情况下,至少扔几次鸡蛋,找到鸡蛋恰好不会碎的楼层

最坏情况:不管用什么策略,鸡蛋破碎一定发生在穷举尽搜索空间的时候。
比如总共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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值