Leetcode 雞蛋掉落多解法

本文是學習Leetcode 雞蛋掉落的筆記,只是一篇我的筆記,排版相對粗糙 hhh ^_^javascript

若是您對某個地方感興趣,我又講得不清楚,歡迎評論,我會從新整理java

表達不當或理解不對的地方,感謝您點評指正~markdown

1、初始值

  1. eggs K=0 或 floors N=0, 結果爲0,由於沒有雞蛋能夠扔,或者有雞蛋沒有樓層扔,只能等於 0
  2. floors N=1, eggs K >= 1, 結果爲 1,由於只能扔一次,只能在floor 1 樓扔 1 <= X <= N = 1
  3. eggs K=1, floors N >= 1, 結果爲 N,由於只能從下往上,由於只有一個蛋

2、推導遞歸公式(遞推公式)

先後兩個蛋之間存在某種必然的聯繫~app

此時此刻手上的蛋在第幾層扔,會對 F 產生的影響函數

eggs floors
        superEggDrop(K,    N)
        
        K, N 的條件下,在 X 層扔,會出現的情形
            1. 雞蛋不碎: superEggDrop(K, N-X)    // X 層和如下的不須要驗證了,N-X 範圍是 (X, N]
            2. 雞蛋碎了: superEggDrop(K-1, X-1)  // X 層如下的樓層,X 已經扔過,因此是 [0, X-1]

        以 superEggDrop(2,  5) 爲例
                            (2,   5)           有2個蛋,須要篩選 5層
              /    /           |          \          \
            /     /            |            \          \
          /      /             |              \          \
    (2,4)(1,0) (2,3)(1,1) (2,2)(1,2)    (2,1)(1,3)    (2,0)(1,4)
     |    |  
一樓沒碎 一樓碎了 ....
[2,5]=4 [0, 1)=0
樓層
複製代碼

不受控制的碎與不碎oop

真實狀況,可能碎,也可能不碎,不受咱們控制,可是真實狀況只可能兩種中的一種學習

取兩種中的最大值,能夠保證計算獲得的步數,必定能獲得樓層 Fui

Math.max(superEggDrop(K-1, X-1), superEggDrop(K, N-X))spa

能夠作的選擇,在哪個樓層(X)扔code

superEggDrop(K-1, X-1), superEggDrop(K, N-X) 中 X 範圍是 [0, N],至關於有 2N個子樹

雖然不知道它們等於多少,可是能夠當它們是一個個已知的總體,交給 遞歸 去解決~~~

接下來,選擇那個須要的步數最少的,能夠獲得最優解,能夠獲得遞歸公式

for (let i = 1; X <= N; X++) {
    superEggDrop(K, N) = Math.min(superEggDrop(K, N), Math.max(superEggDrop(K-1, X-1), superEggDrop(K, N-X)))
}
複製代碼

遞推公式 將 superEggDrop 改成 DP 便可

for (let X = 1; X <= n; X++) {
    DP[k][n] = Math.min(DP[k][n], Math.max(DP[k-1][X-1], DP[k][n-X]) + 1)
}
複製代碼

3、二分查找的應用

函數 y = DP[k-1][x-1] x ∈ [1, n]
趨勢 DP[k-1][x-1] 中 k-1 個雞蛋下,x-1 樓層增長, DP[k-1][x-1] 只會遞增或保持不變 (樓層增長了,須要辨別的更多,可能保持不變或遞增)

函數 y = DP[k][n-X] x ∈ [1, n]
趨勢 DP[k][n-X] 中 k 個雞蛋下,X 增長,n-X 表示樓層不斷減小, DP[k-1][x-1] 只會遞減或保持不變

            y = DP[k][n-X]          y = DP[k-1][x-1]
                    \                  /
                      \               /
                        \            /
                          \         /
                            \  —— —— 
                             /\         —— —— —— —— —— 這個點就是所求
                            /  \
                      —— ——     \ 
                     /           —— ——
                    /                  \
複製代碼

補充了我對二分查找的理解

之前整理了使用二分查找3個條件:索引、有序、靜態
此次發現「有序」,不必定是從小到大或從大到小,每一個索引上的值,都能判斷當前索引偏左了,或者偏右了,或者恰好命中,也可使用二分查找

let nextStep = Infinity;
for (let X = 1; X <= n; X++) {
    nextStep = Math.min(nextStep, Math.max(DP[k-1][X-1], DP[k][n-X]) + 1)
}
複製代碼

改成二分查找

let l = 0,
    r = nums.length - 1;
while (l < r) {
    const mid = l + ((r - l)>>1);
    if (nums[mid] > nums[mid + 1]) {
        r = mid;
    } else {
        l = mid + 1;
    }
}
return l;
複製代碼

附:個人二分查找模板代碼

const bsearch = (nums, target) => {
    let left = 0;
    let right = nums.length - 1;

    while (left <= right) {
        let mid = left + ((right - left) >> 1);
        if (nums[mid] == target) {
            return mid;
        } else if (nums[mid] < target) {
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }
    return -1;
}
複製代碼

4、多解法

1.遞歸

var superEggDrop = function(K, N) {
    if (K === 0 || N === 0) { return 0; }
    if (K === 1) { return N; }
    if (N === 1) { return 1; }
    let nextStep = Infinity;
    for (let X = 1; X <= N; X++) {
        nextStep = Math.min(nextStep, Math.max(superEggDrop(K-1, X-1), superEggDrop(K, N-X)) + 1)
    }
    return nextStep;
};
複製代碼

2. 動態規劃

var superEggDrop = function(K, N) {
    // 0. 初始化dp容器
    const DP = Array(K+1).fill(null).map(_ => Array(N+1).fill(Infinity));
    // 1. 初始化邊界值
    DP[0][0] = 0;
    for (let k = 1; k <= K; k++) {
        DP[k][0] = 0;
        DP[k][1] = 1;  // N = 1
    }
    for (let n = 1; n <= N; n++) {
        DP[0][n] = 0;
        DP[1][n] = n;  // K = 1
    }
    // 2. 狀態轉移
    for (let k = 2; k <= K; k++) { 
        for (let n = 2; n <= N; n++) {
            let nextStep = Infinity;
            for (let X = 1; X <= n; X++) {
                nextStep = Math.min(nextStep, Math.max(DP[k-1][X-1], DP[k][n-X]) + 1)
            }
            DP[k][n] = nextStep;
        }
    }
    // console.log('DP: ', DP);
    return DP[K][N];
};
複製代碼

3. DP + 二分查找

/** * 解三:在DP的基礎上,二分查找的靈活應用 * @param {number} K eggs * @param {number} N floors * @return {number} */
var superEggDrop = function(K, N) {
    // 0. 初始化dp容器
    const DP = Array(K+1).fill(null).map(_ => Array(N+1).fill(Infinity));
    // 1. 初始化邊界值
    DP[0][0] = 0;
    for (let k = 1; k <= K; k++) {
        DP[k][0] = 0;
        DP[k][1] = 1;  // N = 1
    }
    for (let n = 1; n <= N; n++) {
        DP[0][n] = 0;
        DP[1][n] = n;  // K = 1
    }
    // 2. 狀態轉移
    for (let k = 2; k <= K; k++) { 
        for (let n = 2; n <= N; n++) {
            // let nextStep = Infinity;
            // for (let X = 1; X <= n; X++) {
            // nextStep = Math.min(nextStep, Math.max(DP[k-1][X-1], DP[k][n-X]) + 1)
            // }
            // 改寫爲二分查找
            const eggBreak = (x) => (DP[k-1][x-1]); // /
            const notBreak = (x) => (DP[k][n-x]);   // \
            let l = 1,
                r = n;
            while (l <= r) {
                const mid = l + ((r - l)>>1);
                if (eggBreak(mid) === notBreak(mid)) {
                    l = mid;
                    break;
                }
                if (eggBreak(mid) > notBreak(mid)) {
                    r = mid - 1;
                } else {
                    l = mid + 1;
                }
            }
            // return l;
            DP[k][n] = Math.max(notBreak(l), eggBreak(l)) + 1;
        }
    }
    // console.log('DP: ', DP);
    return DP[K][N];
};
複製代碼

題目

Leetcode-887. 雞蛋掉落

/*
 * @lc app=leetcode.cn id=887 lang=javascript
 *
 * [887] 雞蛋掉落
 *
 * https://leetcode-cn.com/problems/super-egg-drop/description/
 *
 * algorithms
 * Hard (28.67%)
 * Likes:    497
 * Dislikes: 0
 * Total Accepted:    32.1K
 * Total Submissions: 111.9K
 * Testcase Example:  '1\n2'
 *
 * 你將得到 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
 * 
 * 
複製代碼

參考資料

相關文章
相關標籤/搜索