鹰蛋问题解析之动态规划

来源:互联网 发布:php让接收的图片倾斜 编辑:程序博客网 时间:2024/05/02 02:00

一幢 100 层的大楼,给你两个鸡蛋。如果在第 n 层扔下鸡蛋,鸡蛋不碎,那么从第 n-1 层扔鸡蛋,都不碎。这两只鸡蛋一模一样,不碎的话可以扔无数次。最高从哪层楼扔下时鸡蛋不会碎?


1. 如果有无数个蛋


如果问题分为两问,第一问提出如果你鹰蛋有无数个,该如何求解?这个问题比较简单,只需要二分法就能在O(lgn)的次数内求解问题,问题的第二问是如果只有两个鹰蛋,该如何求解,我当时的给的答案如下:


2.一种方案,把楼层等分试探求解


把楼层分为x等分,用第一鹰蛋从下往上依次试探一个范围,如果第一个鹰蛋破了,则用另一鹰蛋穷举。

假如将100层楼分为20等分,则用第一个鹰蛋分别在第5层,第10层,第15层。。。。依次试探,假如在35层鹰蛋破碎,则用另一个鹰蛋从31层到34层依次试探,则可以求出破蛋的临界点。

上面的方法的最坏情况是鹰蛋的临界点在N-1层(N代表总楼层数),也就是倒数第二层。因为这时候第一个蛋把所有的等分楼层都尝试了一遍,而且第二个蛋也要把一个等分内部的楼层全部尝试一遍。

假设把总楼层分成了X等份,每个等分内部有N/X个楼层。

在最坏情况下,第一个蛋需要试探X次,第二蛋则要试探N/X - 1次(即在每个等份内做穷举),所以最坏情况需要的总次数为X + (N/X) -1。

要获取最坏情况的最小值,需要对总次数X + (N/X) -1求导数,并取0值,即:


(X + (N/X) -1)'(求导)

=1- (N/X2

=0

求解,可以得到X = sqrt(N)。

在楼层=100的情况下,可以求出使总次数最小的X=10,也就是说如果采用等份的办法,在楼层总数是100时,10等份是最优情况。


3.最终答案:动态规划


鹰蛋问题的最优解,可以通过动态规划的办法来实现,假设有m楼层,n个鹰蛋,则在第i层试探时会出现两种状态,一种状态是鹰蛋摔破了,则我们下一步只有n-1个鹰蛋,同时总楼层数也缩减为i-1,另一种状态是鹰蛋没有摔破,那么鹰蛋总数不变,还是n个,楼层数则缩减为m-i层。

这样一个问题就被分解为两个规模更小的子问题,通过递归的方式求解,递归在以下3个状态结束:1)如果鹰蛋只剩1个,那么只能对所有的楼层进行穷举;2)如果楼层是0,则需要试探0次; 3)如果楼层是1,则需要只需要试探1次。


动态规划的状态转移方程如下:

F(m,n)= MIN{ MAX{ F(i-1, n-1) + 1, F(m-i, n)+1}};(0 < i < m)

通过方程可以看出,再递归的过程中会重复的解子问题,通过array[M][N]来保存子问题的结果,提高效率,代码如下:


[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. const int nfloor = 100;  
  2. const int negg = 2;  
  3.   
  4. #define MAX(a, b) (a) > (b) ? (a) : (b)  
  5.   
  6. int arr[nfloor][negg];  
  7.   
  8. int test_egg(int nfloors, int neggs)  
  9. {     
  10.     if(neggs <= 1)  
  11.         return nfloors;  
  12.   
  13.     if(nfloors == 0)  
  14.         return 0;  
  15.   
  16.     if(nfloors == 1)  
  17.         return 1;  
  18.   
  19.     int min = nfloor;  
  20.   
  21.     if(arr[nfloors-1][neggs-1] != 0)  
  22.         return arr[nfloors-1][neggs-1];  
  23.   
  24.     for(int i = 1; i < nfloors; i++)  
  25.     {  
  26.         int a = test_egg(i-1, neggs-1) + 1;   
  27.         int b = test_egg(nfloors - i, neggs) + 1;  
  28.         int v = MAX(a, b);  
  29.   
  30.         if(min >  v)  
  31.             min = v;  
  32.     }  
  33.   
  34.     arr[nfloors-1][neggs-1] = min;  
  35.   
  36.     return min;  
  37.   
  38. }  
  39.   
  40. int main(int argc, _TCHAR* argv[])  
  41. {  
  42.   
  43.     memset(arr, 0, nfloor*negg*sizeof(int));  
  44.     int a = test_egg(nfloor,negg);  
  45.   
  46.     return 0;  
  47. }  
    转载地址:http://blog.csdn.net/shujiezhang/article/details/28688729
1 0