【刷题】面筋-两颗鸡蛋测临界楼层的问题

题目

  • 有一栋楼,共100层。html

  • 定义:鸡蛋在第n层楼扔下,不会碎,第n+1层扔下,会碎,那么第n层就叫临界楼层(即最高的安全楼层)面试

  • 你手中有两个鸡蛋(默认理想状态:两个鸡蛋彻底相同),如何优化尝试策略,使得使用最少次数,测出临界楼层安全

  • 即,使用此策略,最差也能够在多少次之内测出临界楼层测试

  • (ps:假定鸡蛋必定会在某层楼下落后碎掉)优化

思路1:暴力法遍历

  • 为了避免让鸡蛋碎掉,咱们从一楼开始测试,这样只须要一个鸡蛋,当到达临界楼层的上一层n+1时,鸡蛋碎了,而后咱们能够得出n层是临界楼层,这是最原始的方法,遍历。.net

  • 这种策略最糟糕的状况会是测试到100楼鸡蛋才会碎,测试次数是100次,临界楼层是99楼。设计

  • 最好的状况是测试到2楼鸡蛋就碎了,测试次数是2次,临界楼层是1层。code

思路2:二分查找

  • 咱们手中有俩鸡蛋,为了充分利用条件,咱们能够利用第一个鸡蛋来缩小范围。htm

  • 先跑到50楼去扔,没碎的话,再去75楼去扔···直到第一个鸡蛋碎掉。blog

  • 若是咱们从50楼扔,没碎,说明50楼如下是安全的,50楼以上还有50楼,那咱们再去上面的50楼的一半——75楼去扔,在75楼碎了,说明临界楼层在50层~74层之间,咱们就利用第二个鸡蛋,遍历51层到74层。这是运用二分法。

  • 这种策略最糟糕的状况会是50层鸡蛋就碎了,49层是临界楼层,测试次数是1+49次。

  • 最好的状况是1楼是临界楼层,测试次数是1+2次。

思路3:平均分组

  • 尝试使每一个鸡蛋的测试任务大体至关,即给100开个平方根,第一个鸡蛋只测试整十楼层,第二个鸡蛋测试两个整十楼层之间的楼层。咱们能够先测10楼,20楼,30楼···,直到第一个鸡蛋碎掉。

  • 若是咱们测到30楼,第一个鸡蛋碎了,那咱们就用第二个鸡蛋遍历测试21~29楼。

  • 这种策略最糟糕的状况会是99层是临界楼层,测试次数是10+9次。

  • 最好的状况是1楼是临界楼层,测试次数是1+2次。

思路4:非平均分组

  • 咱们假定存在一种最优策略,最多n次测试就能找到临界楼层。那么,最糟糕的情况会是哪种呢?

  • 从上面的分析能够看出,测试次数分为两部分

    • 第一颗鸡蛋的测试次数x,用来缩小范围
    • 第二颗鸡蛋用来在小范围内查找临界楼层y
    • 即x+y=n
  • 那么当测试次数固定为n时,每当x增长1,y则减小1

    • 即,每当第一颗鸡蛋测试一次,那么所留给第二颗鸡蛋探查的范围就应该减小1
  • 例如:

    • 若是n=10,那么第一次探查了20楼,使用了一次机会,若是碎了,肯定的范围是1~19,那么,第二颗鸡蛋须要使用10-1次机会去探查19层,在最糟糕的情形下显然没法完成。

    • 显然当n=10时,第一次探查为10楼显然更合适,肯定下来的范围是1~9,第二颗鸡蛋使用10-1次探查9层楼,最糟糕的情形下也能知足。

    • 若是探查10楼后鸡蛋没碎,而在第二次探查时碎了呢?咱们第二次探查应该把范围再缩小1,若是第一个鸡蛋多探查一次,那么留给第二颗鸡蛋的探查机会就少一次,咱们要保证在最糟糕的情形下也能探查到,因此留给第二颗鸡蛋的探查范围应该与其探查机会相等。即第一颗鸡蛋的第二次机会应该探查第19层,肯定下来的范围须要探查的范围是11~18,第二颗鸡蛋的剩余探查次数恰好为10-2,匹配成功

    • ·····

    • 根据以上分析,咱们能够发现,每一次的探查范围都减一,即n-1,n-2,n-3,....2,1,0

    • 最后咱们的探查范围会缩小到0

    • 那么咱们把这些探查范围加起来,再加上n就是咱们的探查总范围

  • 即n+(n-1)+(n-2)+······+3+2+1+0=100

    • 咱们先假设最坏状况下,鸡蛋下落次数为x,即咱们为了找出N,一共用鸡蛋作了x次的实验。

    • 假设第一次是在第y层楼扔的鸡蛋, 若是第一个鸡蛋在第一次扔就碎了,咱们就只剩下一个鸡蛋,要用它准确地找出N, 只能从第一层向上,一层一层的往上测试,直到它摔坏为止,答案就出来了。

    • 因为第一个鸡蛋在第y层就摔破了, 因此最坏的状况是第二个鸡蛋要把第1到第y-1层的楼都测试一遍,最后得出结果, 噢,原来鸡蛋在第y-1层才能摔破(或是在第y-1层仍没摔破,答案就是第y层。) 这样一来测试次数是1+(y-1)=x,即第一次测试要在第x层。

    • OK, 那若是第一次测试鸡蛋没摔破呢,那N确定要比x大,要继续往上找,须要在哪一层扔呢? 咱们能够模仿前面的操做,若是第一个鸡蛋在第二次测试中摔破了, 那么第二个鸡蛋的测试次数就只剩下x-2次了(第一个鸡蛋已经用了2次)。 这样一来,第二次扔鸡蛋的楼层和第一次扔鸡蛋的楼层之间就隔着x-2层。

    • 咱们再回过头来看一看,第一次扔鸡蛋的楼层在第x层,第1层到第x层间共x层; 第1次扔鸡蛋的楼层到第2次扔鸡蛋的楼层间共有x-1层(包含第2次扔鸡蛋的那一层), 同理继续往下,咱们能够得出,第2次扔鸡蛋的楼层到第3次扔鸡蛋的楼层间共有x-2层, ……最后把这些互不包含的区间数加起来,应该大于等于总共的楼层数量100,即x + (x-1) + (x-2) + ... + 1 >= 100

    • 注:算式中的项表示的是第一颗鸡蛋走的层数,所以是从n开始。

  • 解恰好为正整数14,即便用此策略最多探查14次便可在100楼中找到临界楼层

  • 另外,当探查总范围发生改变时,解的n可能为小数,显然,探查次数只能为整数且n越小探查总范围越小,

  • 即n应向上取整

  • 我先用第1个鸡蛋在如下序列表示的楼层数不断地向上测试,直到它摔破。 再用第2个鸡蛋从上一个没摔破的序列数的下一层开始,向上测试, 便可保证在最坏状况下也只须要测试14次,就能用2个鸡蛋找出从哪一层开始, 往下扔鸡蛋,鸡蛋就会摔破。

  • 14, 27, 39, 50, 60, 69, 77, 84, 90, 95, 99, 100

    • 好比,我第1个鸡蛋是在第77层摔破的,那么我第2个鸡蛋就从第70层开始,向上测试, 第二个鸡蛋最多只须要测试7次(70,71,72,73,74,75,76),加上第1个鸡蛋测试的 7次(14,27,39,50,60,69,77),最坏状况只须要测试14次便可得出答案。

代码实现

  • 这个问题还有一个泛化的版本,即d层楼,e个鸡蛋,而后设计方案找出N, 使最坏状况下测试的次数最少。这个要用动态规划(DP)来解。

  • f[d][e]表示d 层楼,e个鸡蛋时,最坏状况下的测试次数,则:

  • f[d][e]=min{max(f[d-i][e]+1,f[i-1][e-1]+1)},i=1,2,...,d;

  • f[k][1]=k,0<=k<=d,f[0][0...e]=0;

  • 实现代码以下:

int min_testnumber(int d, int e)  
{  
        int **f=new int *[d+1];  
        int i,j,k;  
        for(i=0;i<=d;i++)  
            f[i]=new int[e+1];  
        for(i=0;i<=d;i++)  
            f[i][1]=i;  
        for(i=0;i<=e;i++)  
            f[0][e]=0;  
        for(i=1;i<=e;i++)  
        {  
            for(j=1;j<=d;j++)  
            {  
                int tmp;  
                int min_test=0x7FFFFFFF;  
                for(k=1;k<=j;k++)  
                {  
                    tmp=f[j-k][i]+1>f[k-1][i-1]+1?f[j-k][i]+1:f[k-1][i-1]+1;  
                    if(tmp>min_test)  
                        min_test=tmp;  
                }  
                f[j][i]=min_test;  
            }  
       }  
       return f[d][e];  
}

扩展

  • 若是咱们有三个鸡蛋,有k次机会,咱们最大能够测试多少层楼?

    • 思路同前面同样,第一次测试,不能过高也能过矮,必须恰到好处,也就是第一枚鸡蛋若是破碎,剩余k-1次机会能将剩余楼层给测试完。

    • 由上面结论,k-1次机会最多能够测试k(k-1)/2层楼,因此第一次在k(k-1)/2+1层楼,第一次若是第一枚鸡蛋不碎,第二次在此基础上增长(k-1)(k-2)/2+1层楼,因而,三个鸡蛋k次机会总共测试楼层数为

    • k=9.

  • 至于四个鸡蛋,五个鸡蛋,以致于M个鸡蛋,能够以此类推,方法同上。

参考连接

END

相关文章
相关标签/搜索