DP11 高楼扔鸡蛋问题 Egg Dropping Puzzle @geeksforgeeks
100层楼,两个鸡蛋。某层之上扔鸡蛋就会碎。问至少要测试多少次才能找出这层楼来?
思路:
首先要理解清楚题意,本题不是要找在哪一层以上会把鸡蛋扔破!而是我们假设在W层以上会把鸡蛋仍破,现在问至少要测试Y次才能找到这个W层?求的是Y
另外一个要注意的是本题中要基于扔的人运气最差的情况,即要保证在worst case下也能找到。
定义W层位蛋破层,则
我们第一次从第x层扔蛋下去,有两种可能:
1) 蛋破了:这说明了两点 1.我们手上仍然有n-1颗蛋 2.蛋破层一定在1...x-1之间,这里共有x-1-1+1 =>x-1数量的嫌疑楼层
2)蛋没破:这也说明了两点 1.我们手上仍然有n颗蛋 2.蛋破层一定在x+1...k之间,这里共有k-(x+1)+1 =>k-x数量的嫌疑楼层
定义eggDrop(n, k)为在最差情况下找出蛋破层所需要的最少扔数。n为蛋的数量,k为要检查的连续的楼层的数量
因为我们要基于最差情况,所以我们要选择两种可能之间更坏的那种,即导致扔的数量更多的那种情况,
即max(eggDrop(n - 1, x - 1), eggDrop(n, k - x))
然后我们遍历所有楼层,要找出能使得最后扔的数量最小的那个初始楼层,因此
eggDrop(n, k) = 1 + min{max(eggDrop(n - 1, x - 1), eggDrop(n, k - x)): x in {1, 2, ..., k}}
加1是因为在第x层测试时也消耗了1次。
package DP; /** * 100层楼,两个鸡蛋。某层之上扔鸡蛋就会碎。问至少要测试多少次才能找出这层楼来 * 我们可以决定怎么扔(min),但必须假设我们的运气最差(max) */ public class EggDroppingPuzzle { public static void main(String[] args) { int n = 2, k = 20; System.out.println(eggDropDP(n, k)); System.out.println(eggDropRec(n, k)); } /* Function to get minimum number of trails needed in worst case with n eggs and k floors */ // n: 手上完好蛋的数量 // k: 要测试的连续楼层的数量 public static int eggDropRec(int n, int k){ // If there are no floors, then no trials needed. OR if there is // one floor, one trial needed. if(k==1 || k==0){ return k; } // We need k trials for one egg and k floors if(n == 1){ return k; } int min = Integer.MAX_VALUE; /** 我们要决策怎么扔使得总扔数最少(即使在我们运气最差的情况下)worst-case 所以在每一楼都测试第一扔,然后递归计算出总共需要的扔数,找到扔数最小的数量 所以假设在第x层扔第一个蛋: 结果1:蛋破了->现在我们只有n-1个蛋,还要测试x-1数量的楼层[1,x-1] -> 数量=x-1 -1 + 1 = x-1 结果2: 但没破 -> 现在我们仍有n个蛋,还要测试k-x数量的楼层[x+1,k] -> 数量=k - (x+1) + 1 = k-x */ for(int x=1; x<=k; x++){ int res = Math.max(eggDropRec(n-1, x-1), eggDropRec(n, k-x)); min = Math.min(min, res); } return min+1; // +1是因为测试当前层消耗了一次扔 } /* Function to get minimum number of trails needed in worst case with n eggs and k floors */ // Time Complexity: O(nk^2), Auxiliary Space: O(nk) public static int eggDropDP(int n, int k){ /* A 2D table where entery dp[i][j] will represent minimum number of trials needed for i eggs and j floors. */ int[][] dp = new int[n+1][k+1]; // We need one trial for one floor and0 trials for 0 floors for(int i=1; i<=n; i++){ dp[i][1] = 1; dp[i][0] = 0; } // We always need j trials for one egg and j floors. for(int j=1; j<=k; j++){ dp[1][j] = j; } // Fill rest of the entries in table using optimal substructure // property for(int i=2; i<=n; i++){ // i eggs for(int j=2; j<=k; j++){ // j floors dp[i][j] = Integer.MAX_VALUE; for(int x=1; x<=j; x++){ // try every floor from 1...j int res = 1 + Math.max(dp[i-1][x-1], dp[i][j-x]); dp[i][j] = Math.min(dp[i][j], res); } } } return dp[n][k]; } }