動態規劃——LeetCode困難題 887. 雞蛋掉落


LeetCode每日一題打卡✔️
今天出了這道題,我來試着寫題解✋

題目

題目來源:LeetCode困難題 887. 雞蛋掉落🥚🐣

題目來源:LeetCode困難題 887. 雞蛋掉落

題目描述

你將獲得 K 個雞蛋,並可以使用一棟從 1 到 N 共有 N 層樓的建築。

每個蛋的功能都是一樣的,如果一個蛋碎了,你就不能再把它掉下去。

你知道存在樓層 F ,滿足 0 <= F <= N 任何從高於 F 的樓層落下的雞蛋都會碎,從 F 樓層或比它低的樓層落下的雞蛋都不會破。

每次移動,你可以取一個雞蛋(如果你有完整的雞蛋)並把它從任一樓層 X 扔下(滿足 1 <= X <= N)。

你的目標是確切地知道 F 的值是多少。

無論 F 的初始值如何,你確定 F 的值的最小移動次數是多少?

示例 1:

輸入:K = 1, N = 2 輸出:2
解釋: 雞蛋從 1 樓掉落。如果它碎了,我們肯定知道 F = 0 。 否則,雞蛋從 2 樓掉落。如果它碎了,我們肯定知道 F = 1 。 如果它沒碎,那麼我們肯定知道 F = 2 。 因此,在最壞的情況下我們需要移動 2次以確定 F 是多少。

示例 2:

輸入:K = 2, N = 6 輸出:3

示例 3:

輸入:K = 3, N = 14 輸出:4

提示:

1 <= K <= 100
1 <= N <= 10000

來源:力扣(LeetCode) 鏈接:https://leetcode-cn.com/problems/super-egg-drop
著作權歸領釦網絡所有。商業轉載請聯繫官方授權,非商業轉載請註明出處。

題目真不科學,你的雞蛋質量也忒好了,是鐵蛋吧😂
這是Google以前的經典動態規劃面試題。
另外,b站李永樂老師也有講解,能幫助理解題意,而且最後老師把狀態轉移方程給出來了。視頻中他從雙蛋問題🥚🥚擴展到本題目K個蛋🥚。可以看看:

參考視頻

b站李永樂老師講解視頻:《復工復產找工作?先來看看這道面試題:雙蛋問題》

視頻中最終的板書截圖:
b站李永樂老師

雖然李永樂老師的視頻裏已經把公式給出來了,我下面再用個人理解結合着官方題解,用文字重複解釋一次,順帶補充。

一般解題思路:DP+二分查找

不用動態規劃行不行?答:在OJ上是不行,暴力解法因爲重複計算多,時間複雜度高,判題會Time Limit Error。
那麼我們怎麼優化呢?請往下看。

做出這道題的關鍵,是解題思路中dp公式的推導,想要熟練掌握的話需要一些刷題經驗。知道了原理之後,實現起來並不難。

沿用題目的定義,K 爲雞蛋數,N 爲樓層數,我們用dp(K,N)表示在狀態 (K,N)下最少需要的步數,當我們從第 X 樓扔蛋的時候,把題目分解爲了蛋碎和不碎的子問題:

  • 狀態 (K−1,X−1),雞蛋碎了一個,樓層數減少到X-1。
  • 狀態(K,N−X),雞蛋不碎,雞蛋的數目不變(注意:不碎的雞蛋是可以重複用的),F 在上方的 N−X 層樓

我們的狀態轉移方程要保證雞蛋碎了之後接下來需要的步數 和 雞蛋沒碎之後接下來需要的步數 二者的 最大值 最小。
我們狀態轉移方程就出來了,別忘了我們一開始扔過一次,所以公式裏要有+1。
來自https://leetcode-cn.com/problems/super-egg-drop/solution/ji-dan-diao-luo-by-leetcode-solution/
(我不熟悉markdown和LaTeX寫公式,也覺得麻煩,直接把官方題解的公式截圖了)

僞代碼

僞代碼實現時候是這樣的:

public int superEggDrop(int K, int N) {
        終止條件:N=0 N=1 返回
	    
	    讀取已有狀態值
	    
        查找交點兩側的值low和high
        
        //狀態轉移方程
        //別忘了我們一開始扔過一次,所以公式裏要有+1
        //Math.min()裏這是求了交點兩邊的值,取兩者中最小的
        int min = 1 + Math.min(
                            Math.max(superEggDrop(K - 1, low - 1), superEggDrop(K, N - low)),
                            Math.max(superEggDrop(K - 1, high - 1), superEggDrop(K, N - high))
                        );
        狀態值放到緩存

        返回狀態值;
    }

知道了dp方程,也不能直接暴力查找,會超時的。

我們可以列一個K與N表格幫助我們理解。(懶得畫圖了,不難理解)。
上述的狀態轉移方程中,dp(K−1,X−1) 是一個隨 X 的增加而單調遞增的函數,dp(K,N−X) 是一個隨着 X 的增加而單調遞減的函數,兩者相交形成一個叉叉,❌。(懶得畫圖了,不難理解)。
我們爲了找到它們的交點,使用二分查找(比線性搜索更快)可以快速尋找到這個交點。
兩個函數都是離散函數,所以我們需要交點左右兩側最近的整數中最小的 。
基礎解法,分析完畢。

上代碼:
class Solution {
    Map<Integer, Integer> cache = new HashMap<>();
    
    public int superEggDrop(int K, int N) {
        if (N == 0) {
            return 0;
        } else if (K == 1) {
            return N;
        }
        //key標識狀態(N,K),如果是計算過的可以直接返回
        Integer key = N * 1000 + K; // K <= 100
        if (cache.containsKey(key))
            return cache.get(key);

        int low = 1, high = N;
        //while()裏是二分查找
        while (low + 1 < high) {
            int mid = (low + high) / 2;
            int t1 = superEggDrop(K - 1, mid - 1);
            int t2 = superEggDrop(K, N - mid);

            if (t1 < t2) {
                low = mid;
            } else if (t1 > t2) {
                high = mid;                
            } else {
                low = high = mid;
            }
        }
        //狀態轉移方程
        //別忘了我們一開始扔過一次,所以公式裏要有+1
        //Math.min()裏這是求了交點兩邊的值,取兩者中最小的
        int min = 1 + Math.min(
                            Math.max(superEggDrop(K - 1, low - 1), superEggDrop(K, N - low)),
                            Math.max(superEggDrop(K - 1, high - 1), superEggDrop(K, N - high))
                        );
        cache.put(key, min);

        return cache.get(key);
    }
}

複雜度分析

時間複雜度:O(K∗Nlog⁡N)。O(K∗N) 個狀態,每個狀態 O(logN) 的時間進行二分搜索。
空間複雜度:O(K∗N)。我們需要 O(K∗N) 的空間存儲每個狀態的解。

其他更高級的解法

此外,有更加高級的解法:
方法二 決策單調性(空間複雜度更小)
方法三 數學法(逆向思維,不容易想到,思路是,若你有 K 個雞蛋,你最多操作 R 次,求 N 最大值。此方式的代碼極簡,一維DP就完事了。dp[k][r] = dp[k][r-1] + dp[k-1][r-1] + 1,第 R 次操作結果只和第 R-1 次操作結果相關,因此可以只用一維數組)
看了逆向思維法,你會覺得之前的代碼不香了😭。
有興趣請自行查看官方題解和其它題解,鏈接附在下面參考資料章節。

參考資料:

[1]LeetCode 887. 雞蛋掉落https://leetcode-cn.com/problems/super-egg-drop/
[2]b站李永樂老師講解視頻:《復工復產找工作?先來看看這道面試題:雙蛋問題》https://www.bilibili.com/video/BV1KE41137PK?from=search&seid=12396330197330656928
[3]LeetCode 887. 雞蛋掉落官方題解https://leetcode-cn.com/problems/super-egg-drop/solution/ji-dan-diao-luo-by-leetcode-solution/
[4]《雞蛋掉落,即鷹蛋問題,圖片詳解,Java擊敗100.00%》作者:wat-2https://leetcode-cn.com/problems/super-egg-drop/solution/zhi-xing-yong-shi-0-ms-zai-suo-you-java-ti-jia-121/

修改記錄

2020年4月12日10:26:22

  • 標題由《Google經典動態規劃面試題–LeetCode困難題 887. 雞蛋掉落》改爲《動態規劃——LeetCode困難題 887.
    雞蛋掉落》
  • 修改了一些不通順的句子
  • 參考資料顯示了鏈接網址

小聲嗶嗶

水平有限,自己得多多練習,還望各位不吝賜教 ☕️
這次文章試着加了點emoji表情😅,希望能讓文章讀起來更舒服點。使用PC網頁閱讀時候的emoji更好看一些。
覺得有用可以點個贊❤️,謝謝

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章