参考博客链接:https://blog.csdn.net/Autumn03/article/details/80886814

题目:2个鸡蛋,从100层楼上往下扔,以此来测试鸡蛋的硬度,比如鸡蛋在第9层没有摔碎而在第10层摔碎了,那么鸡蛋不会摔碎的临界点就是9层,如何用最少的尝试次数,测试出鸡蛋不会摔碎的临界点?

题目中隐藏的含义是两个鸡蛋最多只能先碎一个,碎第二个的时候就要得出最终答案。

第一种方法(枚举求解):首先我们最先想到的方法肯定是用一只鸡蛋直接从第1层尝试到第100层,直到鸡蛋碎了,就知道前一层是临界点,最坏的情况是临界点的值为99时,最多需要尝试100次,最好的情况是当临界点为第1层时,需要尝试2次。(枚举求解)

(为什么是从第1层开始不从100层往下尝试?就是因为如果100开始试,临界点是第1层,那么两只鸡蛋会在100和99层就碎了,求不出结果)

//第一种:枚举
//n表示层数。随机生成一个1~n之间的整数模拟结果,本代码仅用于说明解题思路
#include <bits/stdc++.h>
using namespace std;
int result,ans,n=100;
void meiju(int N){for(int i=1;i<=N;i++){ans++;//扔的次数加1 if(i==result+1)return;}
}
int main(){srand((unsigned)time(NULL)); result=1+rand()%n;meiju(n);cout<<ans;return 0;
}

第二种方法(二分查找):第一种方法我们只需要用到一只鸡蛋就可以了,接下来思考如何利用第二只鸡蛋优化枚举的解题方法,本人首先想到的是使用二分查找进行优化。这种方法最坏的情况是临界点为49层时,第一只鸡蛋先从50层往下扔,如果碎了,第二只鸡蛋就从第1层往上扔,最多尝试的次数是1+49=50。

有人可能会问为什么第二只鸡蛋不在25层扔?因为,如果第二只鸡蛋在25层扔碎了,就不知道临界点具体在哪一层,只能得出临界点的范围是1~24,因此这样不行。

如果第一只鸡蛋在50层扔没碎,证明高度不够高,因此对51+100二分,在75层扔,如果鸡蛋碎了,就在51层开始往上尝试,当临界点为74时,共1+1+24=26层。如此类推下去,最坏的情况就是临界点为49层时,要扔50次,最好的情况是临界点为99层时要抛7次,第一只蛋一直二分都没碎,投掷点的位置为50、75、88、94、97、99、100层。

在第一种方法代码的基础上对代码进行修改:

//二分查找
//n表示层数。随机生成一个1~n之间的整数模拟结果,本代码仅用于说明解题思路
#include <bits/stdc++.h>
using namespace std;
int ans,result,n=100;
void meiju(int start,int end){for(int i=start;i<=end;i++){ans++;//扔的次数加1 if(i==result+1)return;}
}void erfen(int left,int right){//参数表示二分的左右边界 ans++;//抛一次 int mid=(left+right)/2;if(mid>result)//表示从这一层抛会碎 meiju(left,mid-1);elseerfen(mid+1,right);//如果没碎,表示不够高,再从上半段的中点开始抛return;
}
int main(){srand((unsigned)time(NULL)); result=1+rand()%n;erfen(1,n);cout<<ans;return 0;
}

第三种方法(平方根法):使用当前层数的平方根作为循环的步长进行判断,如100的平方根是10,那么就枚举10、20、30……100这些层进行判断,最坏的情况就是当临界点为99层的时候,第一只蛋一直抛到100层才碎,接着从91一直枚举到99层,需要抛10+9=19次。最好的情况是当临界点为10的时候,在第20层的时候就碎了,然后枚举11~20层,第二只蛋在11层就碎了,共抛2+1=3次。

在第一种方法的代码基础上对代码进行修改:

//平方根法
//n表示层数。随机生成一个1~n之间的整数模拟结果,本代码仅用于说明解题思路
#include <bits/stdc++.h>
using namespace std;
int ans,result,n=100;
void meiju(int start,int end){for(int i=start;i<=end;i++){ans++;//扔的次数加1if(i==result+1)return;}
}void pfg(int N){int pfg=sqrt(N);for(int i=pfg;i<=N;i+=pfg){ ans++;if(i>result){//如果从当前平方根层数抛碎了 meiju(i-pfg+1,i-1);//就从上一个平方根的下一层开始抛 return; }}
}int main(){srand((unsigned)time(NULL)); result=1+rand()%100;pfg(n);cout<<ans;return 0;
}

第四种方法(方程法):从上面的平方根方法可以知道,需要寻求到楼层分的段数与每段间隔之间的一个平衡。

各种方法楼层分成段数、每段间隔、最快/最慢找出结果的次数
方法名称 枚举 二分查找 平方根法 方程法
段数 100 7 10 12
每段间隔 1 与2的次方相关 10 间隔从13开始一直递减1
最快次数 2 7 3 3
最慢次数 100 50 19 14
投掷点位置

1 2 3 ……

99 100

50 75 88 94

97 99 100

10 20 30

……

80 90 100

14,27,39,50,

60,69,77,84,

90,95,99,100

那要怎样才能得出上面方程法的段数和每段间隔呢?

假设的答案逆推回去假设问题存在最优解,这个最优解的最坏情况尝试次数是x次。第一次尝试从第x层扔,鸡蛋碎了。那么第二只鸡蛋就要用枚举法从第1层一层层往上尝试扔,最坏的情况要尝试到x-1层,这样总共扔了1+x-1=x次

如果第一只鸡蛋在x层扔没碎的话,考虑第二次扔的位置,其实就是将问题转换为考虑:将两个鸡蛋在100-x层楼往下扔,要求尝试次数不得超过x-1次得出答案,需要怎样扔?第一次我们选择的楼层间隔为次数x,那么第二次我们用一样的方法选择x-1作为楼层间隔,那么投掷点位置为x+(x-1),如果鸡蛋还没有碎,第三次扔的楼层间隔为x-2,投掷点位置为x+(x-1)+(x-2)。如此类推下去,直到间隔为1。因此,得出方程x+(x-1)+(x-2)+(x-3)……+1=100。使用等差数列求和公式并结合题目,即要求出第一个满足条件如下条件的最小整数x的值。(x+1)*x/2>=100。解得:x=14。因此,尝试扔的楼层为14,27,39,50,60,69,77,84,90,95,99,100最好的情况是当临界点为14或15层时,先尝试14和27,发现27层扔时鸡蛋碎了,就从15层一直尝试上去。共扔2+1=3次。最坏的情况是临界点为13层时,先尝试14层没碎,于是从1层尝试到13层,共试了1+13=14次。

在第一种方法的代码基础上对代码进行修改:

//第四种:方程法
//n表示层数。随机生成一个1~n之间的整数模拟结果,本代码仅用于说明解题思路
#include <bits/stdc++.h>
using namespace std;
int result,ans,a[1000000],n=100;
void meiju(int start,int end){for(int i=start;i<=end;i++){ans++;//扔的次数加1if(i==result+1)return;}
}
void fc(int N){int i=0,j=1;for(i=1;(i+1)*i/2<N;i++);int a1=i,d=i-1;//a1表示首项,d表示当前相邻两个投掷点的差 a[0]=0,a[1]=i;//a[0]用于作为边界条件,题目中如果第一次从14层扔就碎了,就要从第1层开始往上尝试 while(a[j]+d<N)a[j+1]=a[j]+d,j++,d--;//生成二级等差数列a[++j]=N;//最后一个点一定为最高那层 for(int k=1;k<=j;k++){ans++; if(a[k]>result){//该层扔鸡蛋碎了meiju(a[k-1]+1,a[k]-1);return;}}return;
}
int main(){srand((unsigned)time(NULL)); result=1+rand()%n;    fc(n);cout<<ans;return 0;
}

第五种方法(动态规划):将题目从常量要求改为变量要求,有m个鸡蛋和n层楼,找到鸡蛋摔不碎的临界点,需要尝试几次?

我们可以先把m个鸡蛋和n层楼的问题转化为一个函数F(m,n),其中鸡蛋数m和楼层数n是函数的两个参数,函数的值则是最大尝试次数,假设第一个鸡蛋扔出的位置在第x层(1<=x<=n),会出现碎和没碎两种情况:

1.没碎,则剩余n-x层楼未尝试和n个鸡蛋,F(m,n-x)+1

2.碎了,则从1到x-1层尝试,剩余鸡蛋m-1,F(m-1,x-1)+1

最终的答案为碎和没碎两种情况中尝试次数中的最小值

F(m,n)= Min(Max(F(m,n-x)+1,F(m-1,x-1)+1))   1<=x<=n

例如:3只鸡蛋,6层楼。

首先填充用一只鸡蛋在各个楼层的尝试次数,以及任意多鸡蛋在1层楼的尝试次数,原因:只有一只鸡蛋,因此只能从第1层顺序扔到最后一层,尝试次数为楼层数量;只有一个楼层,无论有几只鸡蛋,也只有一种扔法,尝试次数只能为1,除此之外,还要定义F(0,0)、F(任何值,0)或F(0,任何值)的值均为0。

 

1层楼

2层楼 3层楼 4层楼 5层楼 6层楼
用一只鸡蛋 F(1,1)=1 F(1,2)=2 F(1,3)=3 F(1,4)=4 F(1,5)=5 F(1,6)=6
用二只鸡蛋 F(2,1)=1          
用三只鸡蛋 F(3,1)=1          

用2只鸡蛋2层楼时,代入状态转移方程式F(m,n)=Max(F(m,n-x)+1,F(m-1,x-1)+1) 得:F(2,2)=Max(F(2,2-x)+1,F(2-1,x-1)+1) ,

此时x可以为1和2:

当x=1时,F(2,2)=Max(F(2,2-1)+1,F(2-1,1-1)+1) =Max(F(2,1)+1, F(1,0)+1)=Max(1+1, 0+1)=2

当x=2时, F(2,2)=Max(F(2,2-2)+1,F(2-1,2-1)+1) =Max(F(2,0)+1, F(1,1)+1)=Max(0+1, 1+1)=2

取两者中的最小值即为2

依次类推

 

1层楼

2层楼 3层楼 4层楼 5层楼 6层楼
用一只鸡蛋 F(1,1)=1 F(1,2)=2 F(1,3)=3 F(1,4)=4 F(1,5)=5 F(1,6)=6
用二只鸡蛋 F(2,1)=1 F(2,2)=2 F(2,3)=2 F(2,4)=3 F(2,5)=3 F(2,6)=3
用三只鸡蛋 F(3,1)=1 F(3,2)=2 F(3,3)=2 F(3,4)=3 F(3,5)=3 F(3,6)=3

C++代码如下:

#include <bits/stdc++.h>
using namespace std;
int a[10000][10000],Egg=2,Floor=100,result;//ans一开始要设置为INT的最大值
void getAns(int e,int f){if(e<1 || f<1){//鸡蛋或者层数其中一个为0,结果为0 result=0;return;}         //创建边界a[0][0]=0;for(int i=1;i<=f;i++){//下标为0和1的行 a[0][i]=0;a[1][i]=i;}for(int i=1;i<=e;i++){//下标为0和1的列a[i][0]=0;a[i][1]=1;}//利用状态转换方程F(m,n)= Min(Max(F(m,n-x)+1,F(m-1,x-1)+1)) 1<=x<=n打表 for(int egg=2;egg<=e;egg++){for(int floor=2;floor<=f;floor++){//定义一个变量来存储最终结果,找到在哪里扔能达到所扔次数最少的目标int ans=INT_MAX;// 分解为还有egg个鸡蛋,一共有floor层楼的子问题// 从第drop层扔鸡蛋for(int drop=1;drop<=floor;drop++){//  碎了,剩下的问题即如何在drop-1层,用egg-1个鸡蛋寻找最优解int broken=a[egg-1][drop-1]+1;//  没碎,在floor-drop层,用egg个鸡蛋找最优解int unbroken=a[egg][floor-drop]+1;// 两种情况取最大值,因为我根本不知道鸡蛋会不会碎int condition=max(broken, unbroken);//   不断和上一次结果做比较,得到最少的扔次数ans=min(condition, ans);}a[egg][floor]=ans;}    }result=a[e][f];
}
int main(){getAns(2,100);cout<<result;return 0;
}
结果对比
方法名称 枚举 二分查找 平方根法 方程法 动态规划
最快次数 2 7 3 3 14
最慢次数 100 50 19 14

【动态规划】2只鸡蛋与100层塔问题(有详细说明和思路代码)相关推荐

  1. N个鸡蛋从M楼层摔(2个鸡蛋从100层摔)

    转载自: http://blog.csdn.net/wolinxuebin/article/details/47057707 http://www.cnblogs.com/yangai/p/53915 ...

  2. 【经典算法题】用两个鸡蛋和100层的楼来测鸡蛋硬度

    前几天看到一个挺有意思的面试问题,据朋友说今年字节和Shopee都问过. 题目如下: 有2个鸡蛋,在总共有100层的楼上往下扔,以此来测试鸡蛋的硬度.比如鸡蛋在第9层没有摔碎,在第10层摔碎了,那么鸡 ...

  3. 动态规划--100层楼2只鸡蛋最少次可以测试最高楼层不摔破

    100层楼2个鸡蛋 原题目:100层楼2个鸡蛋最少需要几次测试,才能得到摔破鸡蛋的楼层: 转换题目:两个鸡蛋,进行k次测试,最多可以测试多少层? 分析:第1个鸡蛋测试所在的楼层高度为k层. ①如果第1 ...

  4. 关于100层楼,扔两个鸡蛋,求摔碎鸡蛋的临界层的问题

    题目描述: 两个软硬程度一样但未知的鸡蛋,它们有可能都在一楼就摔碎,也可能从一百层楼摔下来没事.有座100层的建筑,要你用这两个鸡蛋确定哪一层是鸡蛋可以安全落下的最高位置.可以摔碎两个鸡蛋. 网上有人 ...

  5. C语言丢鸡蛋100层,关于100层楼,扔两个鸡蛋,求摔碎鸡蛋的临界层的问题

    题目描述: 两个软硬程度一样但未知的鸡蛋,它们有可能都在一楼就摔碎,也可能从一百层楼摔下来没事.有座100层的建筑,要你用这两个鸡蛋确定哪一层是鸡蛋可以安全落下的最高位置.可以摔碎两个鸡蛋. 网上有人 ...

  6. 两个软硬程度一样的鸡蛋,它们在某一层摔下会碎,有个100层的建筑,要求最多用两个鸡蛋确 定鸡蛋安全下落的临界位置,给出临界位置?如果是n层楼,m个鸡蛋,请给出确定临界位置的算法

    题目:问题:一幢大楼共计100层,某种类型的鸡蛋从某一楼层及其以上楼层摔下来时会被打破,从该层楼(即临界楼层)以下楼层摔下该鸡蛋,鸡蛋不会出现破损.现给你2个完全一样的该种类型的鸡蛋,问:如何通过这2 ...

  7. 100层摔两个鸡蛋的问题

    这是一道动态规划的题目. 参考两个链接,可以理解解法. 1. http://blog.csdn.net/lzshlzsh/article/details/5951447 2. http://www.c ...

  8. 面试被问扔鸡蛋问题:100层扔鸡蛋问题(扔球问题)

    题目:两个软硬程度一样但未知的鸡蛋,它们有可能都在一楼就摔碎,也可能从任意层楼摔下来没事. 有座100层的建筑(也可能是其他层数),要你用这两个鸡蛋确定最少尝试多少次,可以找出鸡蛋碎裂的最低层. 解法 ...

  9. 逻辑题:100层扔鸡蛋问题(扔球问题)

    题目:两个软硬程度一样但未知的鸡蛋,它们有可能都在一楼就摔碎,也可能从任意层楼摔下来没事. 有座100层的建筑(也可能是其他层数),要你用这两个鸡蛋确定最少尝试多少次,可以找出鸡蛋碎裂的最低层. 解法 ...

最新文章

  1. [机器学习]回归--Decision Tree Regression
  2. win7蓝屏_win7电脑蓝屏怎么办
  3. UVA208Firetruck 消防车(图的路径搜索)
  4. 名企进名校精选IT人 07年毕业生就业看好
  5. MySQL水平分区代理Spock Proxy(一)
  6. 操作系统实践(四/五)
  7. Linux下安装spf13-vim
  8. php 转义字符处理,PHP转义与反转义字符串函数详解
  9. Seeing this, many people find it incredible
  10. Fix Corrupt Blocks on HDFS
  11. HDOJ-1272 小希的迷宫
  12. altium designer怎么添加元件库
  13. 三菱GXWorks2 程序写入CPU/从CPU读取程序
  14. 图片怎么转换成pdf格式?
  15. 配对t检验的应用条件是什么_配对t检验在实际工作中的应用
  16. 查看html源代码编码,查看网页源代码全是乱码,但前台页面可以正常显示
  17. continue在c语言中什么作用,continue在c语言中什么意思?
  18. 微信小程序被投诉怎么办?小妙招教给你
  19. 2021届校招求职流程全解析(IT企业和国企)
  20. Java程序设计的基本结构 - 选择结构

热门文章

  1. Activity启动另一个Activity并返回的完整生命周期
  2. VS2019配置WinRT
  3. 正交db小波 图像处理 matlab,基于matlab小波工具箱的数字图像处理及小波分析
  4. flink 复postgresql数据库数据
  5. 用Java实现简单的井字棋程序(α-β剪枝)
  6. 2020IT从业者如何找到高薪工作
  7. opencv怎么找到手指最高处
  8. 昨天的梦想 今天的幸福
  9. Linux 目录初识
  10. Java算术右移和逻辑右移