樓層扔雞蛋問題[轉]

轉載自:http://www.acmerblog.com/egg-dropping-puzzle-5591.htmlhtml

兩個軟硬程度同樣但未知的雞蛋,它們有可能都在一樓就摔碎,也可能從一百層樓摔下來沒事。安全

現有座36層的建築,要你用這兩個雞蛋肯定哪一層是雞蛋能夠安全落下的最高位置,能夠摔碎兩個雞蛋,要求用最少的測試次數。測試

 

/*
1.若是你從某一層樓扔下雞蛋,它沒有碎,則這個雞蛋你能夠繼續用
2.若是這個雞蛋摔碎了,則你能夠用來測試的雞蛋減小一個
3.全部雞蛋的質量相同(都會在同一樓層以上摔碎)
4.對於一個雞蛋,若是其在樓層i扔下的時候摔碎了,對於任何不小於i的樓層,這個雞蛋都會被摔碎
5.若是在樓層i扔下的時候沒有摔碎,則對於任何不大於i的樓層,這顆雞蛋也不會摔碎
6.從第1層扔下,雞蛋不必定無缺,從第36層扔下,雞蛋也不必定會摔碎。
*/

 

實際上,咱們的終極目的是要找出連續的兩層樓i,i+1。在樓層i雞蛋沒有摔碎,在樓層i+1雞蛋碎了,問題的關鍵之處在於,測試以前,你並不知道雞蛋會在哪一層摔碎,你須要找到的是一種測試方案,這種測試方案,不管雞蛋會在哪層被摔碎,都至多隻須要m次測試,在全部這些測試方案中,m的值最小。優化

爲何是兩個雞蛋呢?若是隻有一個雞蛋,咱們只能從下往上一層一層的測試。對於2個雞蛋,比較容易想到的就是使用二分的方法,如今18層測試,若是這顆碎了,則你從第1層,到第17層,依次用第2顆雞蛋測試。不然繼續用兩個雞蛋測試上半部分的樓層,最多須要18次測試,減小了一半。看似是個不錯的方法,惋惜正確答案是8次。spa

 

把這個問題通常化,考慮n個雞蛋 k層樓,記爲E(n,k)。code

解決的辦法是試着從每一層掉落一個雞蛋(從1到k)並遞歸計算須要在最壞的狀況下須要的最小測試次數。考慮用程序來窮舉全部狀況找到答案。htm

1) 最優子結構blog

當咱們從一個樓層x扔下雞蛋時,有可能出現兩種狀況(1)雞蛋破(2)雞蛋不破。遞歸

1)雞蛋破,那麼咱們只須要用剩下的雞蛋測試 x層如下的樓層; 因此問題簡化爲x-1層和n-1個雞蛋
2)若是雞蛋沒有破,那麼咱們只須要檢查比x較高的樓層; 因此問題簡化爲 k-x 和n個雞蛋。it

 

最優子結構能夠表示爲:

/*
k ==> 樓層數
n ==> 雞蛋數
  eggDrop(n, k) ==>最少須要的測試次數(考慮全部狀況)
  eggDrop(n, k) = 1 + min{max(eggDrop(n - 1, x - 1), eggDrop(n, k - x)): 
                 x 屬於 {1, 2, ..., k}}
*/

 

# include <stdio.h>
# include <limits.h>

int max(int a, int b) { return (a > b)? a: b; }

int eggDrop(int n, int k)
{
    // 基本狀況
    if (k == 1 || k == 0)
        return k;

    //若是隻有一個雞蛋,最壞的狀況下須要k測試
    if (n == 1)
        return k;

    int min = INT_MAX, x, res;

    // 考慮從第1層到底k層扔下雞蛋的全部狀況 的最小結果
    for (x = 1; x <= k; x++)
    {
        res = max(eggDrop(n-1, x-1), eggDrop(n, k-x));
        if (res < min)
            min = res;
    }
    return min + 1;
}

/* 測試 */
int main()
{
    int n = 2, k = 10;
    printf ("\nMinimum number of trials in worst case with %d eggs and "
             "%d floors is %d \n", n, k, eggDrop(n, k));
    return 0;
}

 

重疊子問題

由於上面的程序重複計算了不少子問題。以E(2,4)爲例:

                         E(2,4)
                           |                      
          ------------------------------------- 
          |             |           |         |   
          |             |           |         |       
      x=1/\          x=2/\      x=3/ \    x=4/ \
        /  \           /  \       ....      ....
       /    \         /    \
 E(1,0)  E(2,3)     E(1,1)  E(2,2)
          /\  /\...         /  \
      x=1/  \               .....
        /    \
     E(1,0)  E(2,2)
            /   \
            ......
對於2個雞蛋,4層樓 部分遞歸

 

所以徹底能夠用動態規劃解決。

# include <stdio.h>
# include <limits.h>
int max(int a, int b) { return (a > b)? a: b; }
int eggDrop(int n, int k)
{
    /* eggFloor[i][j] 表示對於 i個雞蛋 j 層樓,須要的最少測試次數 */
    int eggFloor[n+1][k+1];
    int res;
    int i, j, x;
    // 初始化
    for (i = 1; i <= n; i++)
    {
        eggFloor[i][1] = 1;
        eggFloor[i][0] = 0;
    }

    //只有一個雞蛋,沒得優化,須要j次
    for (j = 1; j <= k; j++)
        eggFloor[1][j] = j;

    // 最優子結構的遞推
    for (i = 2; i <= n; i++)
    {
        for (j = 2; j <= k; j++)
        {
            eggFloor[i][j] = INT_MAX;
            for (x = 1; x <= j; x++)
            {
                res = 1 + max(eggFloor[i-1][x-1], eggFloor[i][j-x]);
                if (res < eggFloor[i][j])
                    eggFloor[i][j] = res;
            }
        }
    }
    return eggFloor[n][k];
}

/* 測試*/
int main()
{
    int n = 2, k = 36;
    printf ("\nMinimum number of trials in worst case with %d eggs and "
             "%d floors is %d \n", n, k, eggDrop(n, k));
    return 0;
}
相關文章
相關標籤/搜索