2个鸡蛋,100层楼问题;

题目:

现有两个硬度相同的鸡蛋,以及一栋100层的楼,如果鸡蛋在第n层楼摔下去不会碎,在n-1层楼摔下去会碎,那么鸡蛋的硬度是n,如果要测出鸡蛋的硬度n,在最坏情况下最少要测试几次?每测试一次就把一个鸡蛋从x层楼扔下去;只有两个鸡蛋可用,鸡蛋摔碎了就不能用了;

题目分析:

1:二分????

刚开始看到这个题脑子里最先蹦出来的就是二分;每次取一半,log(n)的算法,然后稍微深入分析一下就悲剧了;

按照二分思路,第一个鸡蛋在50层扔下,没有碎;75层,还没碎;88层,仍然坚强不碎;94层,还是一如既往的坚固;97层,还不碎????!!!;99层,,,,,,100层;7次就测出来了;你这么能咋不直接从100层扔鸡蛋?直接不碎,1次就测出来了。。。

注意题目要求最坏情况;什么是最坏情况?第一个鸡蛋50层就碎了,还剩一个鸡蛋,此时你只能乖乖的从第1层开始测试,不然的话,50层之后直接到25层,第二个鸡蛋也碎了就测不出鸡蛋的强度了;

所以二分在最坏情况下需要测50次:第一个鸡蛋在50层碎了,第二个鸡蛋从第一层测试到49层:1+49=50;

2:分段????

第一次测x层,没破测2*x层,依次递推,如果在i*x层破了,就从(i-1)*x+1层开始一直测到i*x层,

经过计算发现最优的x是8, 9, 10, 11, 12, 13,此时需要测k=19次(100/x+(x-1));

这个好像比较靠谱,19次就能测出来,比二分靠谱多了;难道就没有更好的方法了吗????

3:递推!!!

如果第一次扔鸡蛋,鸡蛋就碎了,那么第二个鸡蛋一定是从1层向上测试,此时如果第一次在第k层楼扔鸡蛋,鸡蛋卒,最坏情况下还需测k-1次,共计k次;现在我们假设最少就需要k次能测出鸡蛋硬度,第一次扔鸡蛋,鸡蛋卒的情况下最优解是k,若第一次鸡蛋生,第二次扔鸡蛋鸡蛋卒最优解也要是k的话,第二次一定在第k+k-1=2*k-1层楼扔鸡蛋,因为在第二次扔鸡蛋,鸡蛋卒后,就需要由第k+1层测试到第2*k-2层,测试了k-2次,共计1+1+k-2=k次;同理,如果第二次扔鸡蛋,鸡蛋未卒,第三次扔鸡蛋,鸡蛋卒,则第三次在第k+(k-1)+(k-2)层扔鸡蛋。。。依次递推当前k-1次鸡蛋都逃出生天,到第k次扔鸡蛋时已经该测试第k+(k-1)+(k-2)+(k-3)+···+1=(k+1)*k/2层楼了,也就是说,此楼最多(k+1)*k/2层才能通过最少k次测出鸡蛋硬度;

综上所述:当(k+1)*k/2>=n时,一定可以用k次测出在n层楼上鸡蛋的强度;

对于一个给定的n,取最小的k满足上式,k就是最优解;

当n=100时,k=14时最优;

4:动态规划!!!!

是不是很惊讶???!!!这样也能DP!!!

若n为楼层总数, 令f[n]为总共n层楼时的最优解;在第i层楼如果鸡蛋碎了, f[i]=i-1+1(从第一层开始测试);如果鸡蛋未碎, f[i]=f[n-i]+1(在剩下的n-i层楼测试最优解);所以:f[i]=max(i-1, f[n-i])+1;

f[n]=min(max(i-1, f[n-i])+1  |  i=1, 2, 3, 4, ···, n);

#include<bits/stdc++.h>
using namespace std;
int main(){
	int f[105];
	f[0]=0;
	for(int i=1; i<=100; i++){
		f[i]=i;
		for(int j=1; j<=i; j++){
			f[i]=min(f[i], max(j-1, f[i-j])+1);
		}
	}	
	cout << f[100] << endl;
	return 0;
}

 

将该题扩展一下, n层楼,m个鸡蛋如何推出最优解?

还是用DP做一下:f[n][m]表示n层楼m个鸡蛋时的最优解;

在第i层,有j个鸡蛋时,本次测试鸡蛋卒,f[i][j]=f[i-1][j-1]+1(用剩下的j-1个鸡蛋测试i-1层),鸡蛋生还f[i][j]=f[n-i][j]+1(继续用j个鸡蛋测试剩下的n-i层);f[i][j]=max(f[i-1][j-1], f[n-i][j])+1;

所以f[n][m]=min(max(f[i-1][j-1], f[n-i][j])+1  |   i=1, 2, 3, 4, ···, n)

#include<bits/stdc++.h>
using namespace std;
int main(){
	int f[105][105];
	for(int i=0; i<=100; i++){
		f[i][1]=i;
	}
	for(int j=2; j<=100; j++)
		for(int i=1; i<=100; i++){
			f[i][j]=i;
			for(int k=1; k<=i; k++){
			f[i][j]=min(f[i][j], max(f[k-1][j-1], f[i-k][j])+1);
			}
	}	
	cout << f[100][2] << endl;
	return 0;
}

 

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值