1300:鸡蛋的硬度
时间限制: 1000 ms 内存限制: 65536 KB
提交数: 2381 通过数: 1623
【题目描述】
最近XX公司举办了一个奇怪的比赛:鸡蛋硬度之王争霸赛。参赛者是来自世界各地的母鸡,比赛的内容是看谁下的蛋最硬,更奇怪的是XX公司并不使用什么精密仪器来测量蛋的硬度,他们采用了一种最老土的办法--从高度扔鸡蛋--来测试鸡蛋的硬度,如果一次母鸡下的蛋从高楼的第a层摔下来没摔破,但是从a+1层摔下来时摔破了,那么就说这只母鸡的鸡蛋的硬度是a。你当然可以找出各种理由说明这种方法不科学,比如同一只母鸡下的蛋硬度可能不一样等等,但是这不影响XX公司的争霸赛,因为他们只是为了吸引大家的眼球,一个个鸡蛋从100 层的高楼上掉下来的时候,这情景还是能吸引很多人驻足观看的,当然,XX公司也绝不会忘记在高楼上挂一条幅,写上“XX公司”的字样--这比赛不过是XX 公司的一个另类广告而已。
勤于思考的小A总是能从一件事情中发现一个数学问题,这件事也不例外。“假如有很多同样硬度的鸡蛋,那么我可以用二分的办法用最少的次数测出鸡蛋的硬度”,小A对自己的这个结论感到很满意,不过很快麻烦来了,“但是,假如我的鸡蛋不够用呢,比如我只有1个鸡蛋,那么我就不得不从第1层楼开始一层一层的扔,最坏情况下我要扔100次。如果有2个鸡蛋,那么就从2层楼开始的地方扔……等等,不对,好像应该从1/3的地方开始扔才对,嗯,好像也不一定啊……3个鸡蛋怎么办,4个,5个,更多呢……”,和往常一样,小A又陷入了一个思维僵局,与其说他是勤于思考,不如说他是喜欢自找麻烦。
好吧,既然麻烦来了,就得有人去解决,小A的麻烦就靠你来解决了:)
【输入】
输入包括多组数据,每组数据一行,包含两个正整数n和m(1≤n≤100,1≤m≤10),其中n表示楼的高度,m表示你现在拥有的鸡蛋个数,这些鸡蛋硬度相同(即它们从同样高的地方掉下来要么都摔碎要么都不碎),并且小于等于n。你可以假定硬度为x的鸡蛋从高度小于等于x的地方摔无论如何都不会碎(没摔碎的鸡蛋可以继续使用),而只要从比x高的地方扔必然会碎。
对每组输入数据,你可以假定鸡蛋的硬度在0至n之间,即在n+1层扔鸡蛋一定会碎。
【输出】
对于每一组输入,输出一个整数,表示使用最优策略在最坏情况下所需要的扔鸡蛋次数。
【输入样例】
100 1 100 2
【输出样例】
100 14
【提示】
最优策略指在最坏情况下所需要的扔鸡蛋次数最少的策略。
如果只有一个鸡蛋,你只能从第一层开始扔,在最坏的情况下,鸡蛋的硬度是100,所以需要扔100次。如果采用其他策略,你可能无法测出鸡蛋的硬度(比如你第一次在第二层的地方扔,结果碎了,这时你不能确定硬度是0还是1),即在最坏情况下你需要扔无限次,所以第一组数据的答案是100。
思路:
这个题目有很多地方都有分析,我也看了好几个解答,但都说得不太明白(也许是我笨),我结合听的讲座和各家分析,加上自己的试验作以下分析。
这时的策略大体如下:第一,最简单的和种情况就是只有一只鸡蛋,那只能从一楼开始一层一层往上走,如果第一楼就破了,那硬度就是0,否则总能找到破与不破的两层。那意味着硬度为多少就得比硬度数多扔一次就好。如果题目给的楼层是100,那最坏的可能就是硬度为99,必须扔100次。(硬度不能为100,题目说了硬度小于n,也意味着从顶楼扔下,鸡蛋一定会碎,但这一次必须要扔)。第二,如果有两枚鸡蛋呢?那第一次就不用在第一楼扔,那在多少楼扔合适?答案是第14楼。那要是破了呢?说明硬度最大13,同时我们手里的鸡蛋也只有一枚了,那办法就只有一个了:从第一楼开始往上,根据刚才的经验,最多再扔13次就好(1-13楼全试,只有一枚鸡蛋,不能冒险哟)。那要是没破呢?又在哪扔呢?第27楼。要是破了,硬度在14-26之间,最多再试12次就可以了,加上之前14楼和27楼两次,共14次。要是没破,下一次选在39楼。同样要是破了,就从28开始往上走,最多走到第38楼,共14次,要是没破,就选50楼。。。。,以后每多一次间距就少一,均能保证14次能测出硬度。第三,为什么是14楼开始?10楼不行么?如果选10楼,要是破了,当然比刚才更快,那要是没破呢?下一次选多少楼?20?然后30、40、50、60、70、80、90?到了60己经扔了6次了,要是破了,最坏的情况是要再扔9次,在是没破又如何继续?明显不是最佳策略。那为什么是14次?按照刚才的策略1+2+3+...+14=105超过100了,说明14次一定能测出结果。第四,要是鸡蛋有更多个呢?比如有三个,第一次可以考虑对分:第50楼,要是不破,再二分,75楼.....,要是破了,就相当于两个鸡蛋测50楼,1+2+3+...+10=55,再有10次就可以搞定了。如果有四个鸡蛋呢?两次就把范围缩小到25楼了,1+2+3+...+7=28,共需9次。那要是鸡蛋无限呢,最多也是全二分,2^7=128,这个就是最效率的了,换句话说,对100层楼而言,多于7个鸡蛋也没用。第五,如果有m个鸡蛋n层楼呢?我们用f[n][m]记最少的次数。那可以枚举从第k层楼扔下,有两种情况:(1)鸡蛋破了,那硬度在1-k-1之间,我们得用余下的m-1个鸡蛋测试k-1层楼,这时f[n][m]=f[k-1][m-1],(2)鸡蛋没破。那再测余下的k~n层楼就好,这时f[n][m]=f[n-k][m],题目要求是最坏的情况,故需要取这两种情况的最大值。故f[n][m]=max(f[k-1][m-1],f[n-k][m])+1。对所有的k的取值得到若干个f[n][m],这个我们可以选择k值,故把这些值再取最小值。f[n][m]=min(f[n][m],max(f[k-1][m-1],f[n-k][m])+1),这就是帅气的状态转移方程了。
代码:
/* 1+2+3+4+..+14>100 每次选择的楼层间数比上一次的楼层间数少一,只要楼层间数和>总楼层数, 则一定可以测出鸡蛋硬度 */ #include<bits/stdc++.h> using namespace std; int n,m; int f[1020][1020]; int main() { while(cin>>n>>m)//楼高,鸡蛋个数 { memset(f,0,sizeof(f)); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++)f[i][j]=i; for(int i=1;i<=n;i++) for(int j=2;j<=m;j++) 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); /*for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) cout<<f[i][j]<<" "; cout<<' '; }*/ cout<<f[n][m]<<' '; } }