参考链接:https://gitee.com/lambertcao/fucking-algorithm/blob/master/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%B3%BB%E5%88%97/%E9%AB%98%E6%A5%BC%E6%89%94%E9%B8%A1%E8%9B%8B%E8%BF%9B%E9%98%B6.md
「状态」很明显,就是当前拥有的鸡蛋数 K
和需要测试的楼层数 N
。随着测试的进行,鸡蛋个数可能减少,楼层的搜索范围会减小,这就是状态的变化。
「选择」其实就是去选择哪层楼扔鸡蛋。回顾刚才的线性扫描和二分思路,二分查找每次选择到楼层区间的中间去扔鸡蛋,而线性扫描选择一层层向上测试。不同的选择会造成状态的转移。
var superEggDrop = function (K, N) {
let memo = {}
let dp = function (K, N) {
// base case
if (K === 1) return N;
if (N === 0) return 0;
// 避免重复计算
let key = K + ',' + N
if (memo[key] !== undefined) {
return memo[key];
}
// 正无穷
let res = Infinity;
// 穷举所有的可能的选择
// for (let i = 1; i < N + 1; i++) {
// res = Math.min(
// res,
// Math.max(
// dp(K, N - i),
// dp(K - 1, i - 1)
// ) + 1
// )
// }
// 用二分搜索代替线性搜索
let lo = 1, hi = N;
while (lo <= hi) {
let mid = Math.floor((lo + hi) / 2);
let broken = dp(K - 1, mid - 1) // 碎
let not_broken = dp(K, N - mid) // 没碎
// res = min(max(碎,没碎) + 1)
if (broken > not_broken) {
hi = mid - 1
res = Math.min(res, broken + 1)
} else {
lo = mid + 1
res = Math.min(res, not_broken + 1)
}
}
// 记入备忘录
memo[key] = res;
return res;
}
return dp(K, N);
};