Leetcode 887. Super Egg Drop
Problem:
You are given K
eggs, and you have access to a building with N
floors from 1
to N
.
Each egg is identical in function, and if an egg breaks, you cannot drop it again.
You know that there exists a floor F
with 0 <= F <= N
such that any egg dropped at a floor higher than F
F
will not break.
Each move, you may take an egg (if you have an unbroken one) and drop it from any floor X
(with 1 <= X <= N
).
Your goal is to know with certainty what the value of F
is.
What is the minimum number of moves that you need to know with certainty what F
F
?
Example 1:
Input: K = 1, N = 2
Output: 2
Explanation:
Drop the egg from floor 1. If it breaks, we know with certainty that F = 0.
Otherwise, drop the egg from floor 2. If it breaks, we know with certainty that F = 1.
If it didn't break, then we know with certainty F = 2.
Hence, we needed 2 moves in the worst case to know what F is with certainty.
Example 2:
Input: K = 2, N = 6
Output: 3
Example 3:
Input: K = 3, N = 14
Output: 4
Note:
1 <= K <= 100
1 <= N <= 10000
Solution:
說道扔雞蛋問題,就不得不提一下那個經典的dp問題。給m個雞蛋嘗試n次,最壞情況下可以在多少樓層內確定會讓雞蛋破裂的最低層數。
對於這道題,我們可以建立一個二維陣列dp,dp[i][j]表示用i個雞蛋嘗試j次可以確定的最高樓層數。這個最高樓層可以由兩部分組成,假設我們在x樓扔下一個雞蛋,它可能會破也可能不會破,如果雞蛋破了,那麼我們需要用剩下的i-1個雞蛋和就-1次機會在x樓之下尋找這個最高樓層,即dp[i-1][j-1],若雞蛋破了,則我們需要在x+1層之上用i個雞蛋嘗試j-1次找到那個最高樓層,即dp[i][j-1],因此可以得到狀態轉移方程:dp[i][j] = dp[i-1][j-1]+1+dp[i][j-1],對於i=1,dp[1][i] = i。程式碼部分如下:
1 class Solution { 2 public: 3 int superEggDrop(int K, int N) { 4 vector<vector<int>> dp(K+1,vector<int>(N+1,0)); 5 for(int j = 0;j <= N;++j) 6 dp[1][j] = j; 7 for(int i = 2;i <= K;++i){ 8 for(int j = 1;j <= N;++j){ 9 dp[i][j] = dp[i-1][j-1]+1+dp[i][j-1]; 10 } 11 } 12 return dp[K][N]; 13 } 14 };
現在來看這道題有什麼不同呢,現在他告訴我們最高樓層,要我們找到最小的嘗試次數,即告訴我們i和dp[i][j]要我們計算j的值。和上面那題的思路類似,我們令x為測試的樓層,在這一樓層扔雞蛋有兩種情況,如果蛋碎了,則我們需要在x-1層裡用i-1個雞蛋找到最小的嘗試次數,如果蛋沒碎,則要在x+1到j層用i個雞蛋找到最小嚐試次數,所以max(dp[i-1][x],dp[i][j-x])即在x層扔下第一個雞蛋的情況下,用i個雞蛋在j層內的最小嚐試次數。所以,我們需要在0-j層內找到合適的x,使得這個最小嚐試次數最少,所以這就成了一個極小化極大值的問題。在這裡其實我們可以觀察到dp[i-1][x]是遞增的,而dp[i][j-x]是遞減的,所以我們在0-j內遍歷x,當dp[i-1][x] == dp[i][j-x]時,可以斷定此時的x即第一個雞蛋應該扔的樓層,所以dp[i][j]就等於dp[i-1][x]+1。
對於這個解法,其時間複雜度為O(kN2),我們可以觀察到在確定x的過程中,我們通過遍歷的方法尋找x,在這個地方我們其實可以通過二分法進行優化,將時間複雜度降低為O(kN*logN)。(雖然通過二分法時間複雜度降低了,但程式的執行時間卻增加了,所以演算法複雜度和程式執行時間真是沒必然聯絡啊)
由於這道題的狀態轉移方程只需要i-1層的資料,其空間複雜度也可以降低為O(N),感興趣的讀者可以繼續優化演算法。
Code:
1 class Solution { 2 public: 3 int superEggDrop(int K, int N) { 4 vector<vector<int>> dp(K+1,vector<int>(N+1,0)); 5 for(int j = 0;j <= N;++j) 6 dp[1][j] = j; 7 for(int i = 2;i <= K;++i){ 8 int x = 1; 9 for(int j = 1;j <= N;++j){ 10 while(x < j && dp[i][j-x] > dp[i-1][x]) 11 x++; 12 dp[i][j] = 1+dp[i][j-x]; 13 } 14 } 15 return dp[K][N]; 16 } 17 };
1 class Solution { 2 public: 3 int superEggDrop(int K, int N) { 4 vector<vector<int>> dp(K+1,vector<int>(N+1,0)); 5 for(int j = 0;j <= N;++j) 6 dp[1][j] = j; 7 for(int i = 2;i <= K;++i){ 8 for(int j = 1;j <= N;++j){ 9 int start = 1; 10 int end = j; 11 while(start < end){ 12 int pivot = start+(end-start)/2; 13 if(dp[i][j-pivot] > dp[i-1][pivot]) 14 start = pivot+1; 15 else 16 end = pivot; 17 } 18 dp[i][j] = 1+dp[i][j-start]; 19 } 20 } 21 return dp[K][N]; 22 } 23 };