1. 程式人生 > >Leetcode 887. Super Egg Drop

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

will break, and any egg dropped at or below floor 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

is, regardless of the initial value of 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. 1 <= K <= 100
  2. 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 };