本文是學習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
*
*
複製代碼