本文由 @lonelyrains 出品,转载请注明出处。
文章链接: http://blog.csdn.net/lonelyrains/article/details/46428569
题目描述: (挑了一个比较严谨的描述。问题描述严谨很重要,不然会影响解题思路)
一幢 100 层的大楼,给你两个鸡蛋. 如果在第 n 层扔下鸡蛋,鸡蛋不碎,那么从前 n-1 层扔鸡蛋都不碎.
这两只鸡蛋一模一样,不碎的话可以扔无数次. 已知鸡蛋在0层扔不会碎.
提出一个策略, 要保证能测出鸡蛋恰好不会碎的楼层, 并使此策略在最坏情况下所扔次数最少.
问题分析:
1)最坏情况下所扔次数最少,比较绕口。想表达的意思是,在不明确知道哪一层会碎的情况下,要找到一种策略,通过最少的试验次数,得到临界楼层(恰好不会碎的楼层)。不明确知道,就需要考虑最糟糕的情况,而且这种策略与其他策略相比是最糟糕的情况下,最少的试验次数。
2)假设一种扔法:第一个鸡蛋,从50楼扔下去。如果碎了,第二个鸡蛋必须从1~49层逐层试验。如果第i层为临界层,且i≤49,这个时候,要试验的总次数是1 +(i - 1)。因为必须保证在没找到临界楼层之前,鸡蛋不能碎。如果没碎,则第一个鸡蛋可以接着从75层扔。因为即使这次碎了,还有个鸡蛋,可以继续逐层试验。对第一个鸡蛋的继续从中间分,就比较合理。
3)假设到代数:如果第一枚鸡蛋扔下去的层数为i,则碎了的情况,需要扔的总次数最糟糕的情况是1 + ( i - 1 );如果没碎,剩下的两个鸡蛋都在,需要扔的次数一定为1 + 用两枚鸡蛋来解决剩下的100 - i层的次数(这个问题跟原题是一样的,但是层数少了一些)。也就是 如果用f ( 100 )表示100层的最坏情况下的最少次数,那么从第i层扔鸡蛋的最糟糕的试验次数是 1+ Max( i - 1, f ( 100 - i ) ),Max表示这两者之间的最大值,是最最糟糕的情况了。 而 f ( 100 ) 就是对所有从1到100的所有i里, 1+ Max( i - 1, f ( 100 - i ) )的值最小的那个。
4)迭代公式: f ( 100 ) = Min ( 1 + Max ( i - 1, f (100 - i ) ) ) . 其中Max是针对的 i-1、 f ( 100 - i ) 两者 , 而Min是针对的所有的从1到100的i。
5)初始状态: 如果有一层,从第一层扔下去,不管碎不碎,最糟糕的情况也只需要判断一次。 即 f ( 1 ) = 1。而如题所述,第0层不会碎,则 不用扔也知道,即f(0) = 0。
6)最终结论:题目变成了分析一个迭代公式的值。翻译成了计算机语言,剩下的就可以交给计算机了。不需要知道怎么一步步算,这不应该是人干的事。只需要知道已经变成了可以循环递归的算式,可以交给计算机就行了。
// 实现代码 <a target=_blank href="http://blog.csdn.net/lonelyrains">blog.csdn.net/lonelyrains</a>
#include <stdio.h>
// #define MAX((a),(b)) (a)>(b)?(a):(b) //注意这里也是常考的一点,应该写成下面的形式
#define MAX(a,b) ((a)>(b)?(a):(b))
int fun ( int layer )
{
if ( layer <= 0 )
{
return 0;
}
if ( layer == 1 )
{
return 1;
}
int min = layer; // 一栋layer层的大楼试验次数肯定不可能超过layer次。
int temp;
for ( int i = 1; i <= layer; i++ )
{
temp = 1 + MAX(i-1, fun( layer - i ) );
if( min > temp )
min = temp;
}
return min;
}
int main()
{
int layer = 19;
printf("%d",fun(layer));
return 0;
}
7)其他扩展:
① 问题的解法不止这一种描述,而且不一定要交给计算机算,因为这样递归算,计算机要累死了。可以优化到用很简单的数列求和公式得到。关于怎么来的,有两种思路。可以参考下面的第二个参考链接给出的基于意义的理解,这是一种思路,但是比较难理解。另外一种,就是纯粹的组合数学方法,将迭代公式转换成通项公式,这个问题还没找到有人这样写过,但是绝对可以有。
② 问题可以扩展为一栋n层的大楼,有m个鸡蛋;甚至不止一栋,而是p栋。 不管怎么样扩展,问题都可以归为找迭代公式。这个思路就是动态规划的精髓。
8)参考链接: