轉載自: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; }