题目
经典问题
有一栋层数为1~100层的高楼,其中存在一个第k层,从这一层将鸡蛋扔下,恰好能够将鸡蛋摔碎(即低于这个楼层,鸡蛋一定摔不碎;高于这个楼层,鸡蛋则一定会摔碎),这个第k层称为临界楼层。
现给了你2个完全一样的鸡蛋,你可以把它们从任意一层楼扔下去。显而易见的,如果你把一个鸡蛋摔碎了,那它就没有了;如果鸡蛋没碎,那还可以捡起来继续扔。在最坏情况下,需要扔多少次鸡蛋才能确定这个临界楼层?
推而广之
- 假如楼高不是100层,而是任意的n层呢?
- 假如鸡蛋不是2个,而是3个?甚至任意的m个?
问题分析
首先明确一件事情:因为题目保证了临界楼层的存在性,因此,临界楼层最大不超过第100层。也就是说,鸡蛋从第100层扔下是肯定会碎的,相当于是题目中隐含的条件。信息完全未知的楼层只有99层。
很显然,这是一个有序的查找问题。
对于查找问题,最简单的就是遍历:从第1层开始扔下鸡蛋,如果没碎,那么依次测试2、3、4……,直到鸡蛋在第k层被摔碎。这固然是一个可行的方法,但是效率很低。在最坏情况下,临界楼层是100层,需要在1~99楼总共扔99次鸡蛋才能得到这一结果。当然,如果你只有1个鸡蛋,那么也就不得不采用这种朴素的办法。
对于这种有序的查找,最有效率的方法自然是二分查找。可惜的是,我们只有2个鸡蛋。假设临界楼层是100层,此时总共需要摔碎8个鸡蛋才能得到结果。虽然二分查找太过于浪费鸡蛋,但是我们可以借鉴其中的思想,采取折衷的策略,在2个鸡蛋的前提下,提高一点效率。
分段查找似乎是一个不错的选择。一个简单的思路是,每10层分一段,总共有10段。第一个鸡蛋依次从10,20,30,……,90层扔下,这样可以确定临界楼层在哪个区间,每个区间长度都是10;再用第二个鸡蛋遍历这个区间,就可以找到临界楼层。最坏情况下,临界楼层在99层或者100层,需要丢18次鸡蛋才能找到结果。
这个做法相比前面的大有长进,不过仍然有提升的空间。这个潜在的提升空间在于,如果第一个鸡蛋更早的碎了,那么有机会用更少的次数找到解;如果第一个鸡蛋碎的比较晚,那么在第一个鸡蛋上已经耗费了相当多的步数。如果能够平衡一下较好的情况和较差的情况,那么将会得到一个最坏情况下表现更好的算法。
解题方法
法一:数学推导
基于上述想法,提出一种思路:对于分段查找的区间,并不采用均匀的划分,而是从下往上每个区间的长度递减1。这样,当临界楼层所在的高度较高时,虽然定位这个区间需要花费更多的次数,但遍历这个区间需要的次数会少一些。1
具体算法如下:
- 第一次从第 x x x层扔下鸡蛋。
- 如果碎了,那么可以确定临界楼层在 [ 1 , x ] [1,x] [1,x]这个区间,区间长度为 x x x。用第二个鸡蛋遍历第 1 1 1层到第 x x x层,寻找临界楼层。
- 如果没碎,那么下一个区间长度应该为 x − 1 x-1 x−1。即下一次在第 x + x − 1 = 2 x − 1 x+x-1=2x-1 x+x−1=2x−1层扔下鸡蛋。
- 如果碎了,可以确定临界楼层在 [ x + 1 , 2 x − 1 ] [x+1,2x-1] [x+1,2x−1]这个区间。
- 如果没碎,那么下一个区间长度应该为 x − 2 x-2 x−2。
- ……

在这种划分方式下,无论临界楼层落在哪个区间,最坏情况下都能够以 x x x步找到。
用如下方程求出100层楼时对应的 x x x(前面提到,因为题目保证了临界楼层的存在性,所以第100层的结果是已知的,完全未知的部分只有99层,因此只需大于等于99):
x + ( x − 1 ) + ( x − 2 ) + . . . + 2 + 1 > = 99 x+(x-1)+(x-2)+...+2+1>=99 x+(x−1)+(x−2)+...+2+1>=99
容易解得 x = 14 x=14 x=14,即最少需要14步,可以保证在最坏情况下找出临界楼层。
法二:动态规划算法
做到这里,我们的经典问题已经解决了。假如想要将题目推广到任意的楼高,也只需要修改上述方程的右边,即可得到结果。
但是,如果变成3个鸡蛋呢?
问题变得麻烦得多了:从第 x x x层扔下第一个鸡蛋,假如碎了,那我用剩下的两个鸡蛋去探索 [ 1 , x ] [1,x] [1,x]这个区间,到底需要多少步?假如没碎,下一个区间又应该设置为多长?
不过,想到这里,我们已经能够看出明显的动态规划特征:问题的解由若干个小规模的子问题的解组成。即:
- 第一次将鸡蛋从 x x x楼扔下,鸡蛋要么碎,要么没碎。
- 如果碎了,那么问题归约到:在 [ 1 , x ] [1,x] [1,x]楼层区间中、用2个鸡蛋寻找临界楼层。
- 如果没碎,那么问题归约到:在 [ x + 1 , 100 ] [x+1,100] [x+1,100]楼层区间中、用3个鸡蛋寻找临界楼层。
- 那么,当第一次将鸡蛋从 x x x楼扔下时,该问题的步数就是这两个子问题各自的最小步数取一个max,再+1(这个+1指的是第一次从 x x x楼扔鸡蛋)。
- 接下来就可以将问题逐层分解。
- 遍历所有可能的 x x x,即可找到整个问题的最小步数。
在这个思路下,来思考相应的状态转移方程,以及如何用程序实现。当拥有m个鸡蛋、楼层高度为n时:
- 定义函数 w o r s t ( i , j ) worst(i,j) worst(i,j),含义是拥有 i i i个鸡蛋、探索一个高度为 j j j的楼层区间,最坏情况下所需要的步数。
- 只有一个鸡蛋的时候,需要通过遍历找解,故 w o r s t ( 1 , j ) = j − 1 worst(1,j) = j - 1 worst(1,j)=j−1(前面说过,因为解的存在性,所以从顶楼扔下鸡蛋是肯定会碎的,可以不扔)。
- 当楼高为1时,临界楼层只能为1,因此 w o r s t ( i , 1 ) = 0 worst(i,1) = 0 worst(i,1)=0。
- 其他情况下,遍历所有的 1 ≤ x < j 1\leq x \lt j 1≤x<j,计算所有的 max { w o r s t ( i − 1 , x ) , w o r s t ( i , j − x ) } + 1 \max\{worst(i-1,x),worst(i,j-x)\}+1 max{ worst(i−1,x),worst(i,j−x)}+1,取其中最小的一种方案为 w o r s t ( i , j ) worst(i,j) worst(i,j)的值。
- w o r s t ( m , n ) worst(m,n) worst(m,n)即为问题的解。
状态转移方程:
w o r s t ( i , j ) = { j − 1 , i = 1 0 , j = 1 min { max { w o r s t ( i − 1 , x ) , w o r s t ( i , j − x ) } + 1 } ,其他,其中 x = 1 , 2 , . . . , j − 1 worst(i,j)=\left\{\begin{matrix}j-1,i=1\\0,j=1\\\min\{\max\{worst(i-1,x),worst(i,j-x)\}+1\},其他,其中x=1,2,...,j-1 \end{matrix}\right. worst(i,

本文探讨了经典的鸡蛋掉落问题,分析了如何在不同条件下使用有限数量的鸡蛋在多层楼中找到摔碎鸡蛋的最低楼层。通过数学推导、动态规划算法、改进动态规划以及二进制串与组合数的方法,逐步优化解决方案,减少最坏情况下的尝试次数。文章详细阐述了每种方法的思路和实现,并提供了相关算法的代码示例。
最低0.47元/天 解锁文章
631

被折叠的 条评论
为什么被折叠?



