本文是学习Leetcode 鸡蛋掉落的笔记,只是一篇我的笔记,排版相对粗糙 hhh ^_^javascript
若是您对某个地方感兴趣,我又讲得不清楚,欢迎评论,我会从新整理java
表达不当或理解不对的地方,感谢您点评指正~markdown
先后两个蛋之间存在某种必然的联系~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)
}
复制代码
函数 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;
}
复制代码
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;
};
复制代码
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];
};
复制代码
/** * 解三:在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];
};
复制代码
/*
* @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
*
*
复制代码