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
 * 
 * 
复制代码

参考资料

相关文章
相关标签/搜索