2019.11.27 LeetCode 从零单刷个人笔记整理(持续更新)
github:https://github.com/ChopinXBP/LeetCode-Babel
这是一道经典的智力题。由于有鸡蛋摔碎的限定条件,因此不能依靠单纯的二分查找来解决,可以从递归开始,不断优化和升级方法。
1.递归(超时)
在手握K个鸡蛋时从第i层楼[0,N]向下丢鸡蛋,会有两种可能:
1.鸡蛋摔碎,将剩余K-1个鸡蛋移动到楼[0,i-1]进行测递归测试。
2.鸡蛋没碎,将剩余K个鸡蛋移动到楼[i,N]进行测试,可将i层看成0层,对[0,N-i]进行递归测试。
递归公式
result = min(max(superEggDrop(K - 1, i - 1), superEggDrop(K, N - i)) + 1)
2.动态规划:二分搜索
观察递归公式
result = min(max(superEggDrop(K - 1, i - 1), superEggDrop(K, N - i)) + 1)
其中i在[1.N]变化。
而fun1=superEggDrop(K - 1, i - 1)随i单调增,fun2=superEggDrop(K, N - i)随i单调减,两条单调函数的交点即为两者最大值的最小取值点。
因此可以对i在[1,N]上进行二分查找。在二分查找过程中,可以设计一个哈希表避免重复计算。
3.动态规划:自底向上
观察转为动态规划的递归公式
dp[i][j] = min(max(dp[i-1][loc-1], dp[i][j-loc]) + 1)
其中i在[1.j]变化,j在[1, N]变化。
而fun1=dp[i-1][loc-1]随loc单调增,fun2=dp[i][j-loc]随loc单调减,两条单调函数的交点即为两者最大值的最小取值点。
寻找loc的思路转换为while循环遍历查找。
令left为loc-1时的函数值,right为loc时的函数值,循环遍历找到最小值的拐点。由于后序j值上限不断增大,loc拐点值只可能逐渐向右移动,以此可以简化后序查找。
3.动态规划(超时):求能够测得的最少楼层
dp[i][j]代表有i个鸡蛋和j次移动时一定能够测得的最少楼层数。没有鸡蛋时或没有移动次数时无法测试dp=0;只有1个鸡蛋但有j次移动,dp=j(从0层开始逐层测试)。
有i个鸡蛋和j次移动时,前j-1次移动的测试结果可能由多种测试路径得来,将其划分为k次和(j-1)-k次遍历取最大。第j-1次移动鸡蛋碎的结果为dp[i-1][k],鸡蛋未碎的结果为dp[i][(j - 1) - k],当前楼层第j次移动的最少测试结果为1(摔碎)。
dp[i][j] = max(dp[i - 1][k], dp[i][(j - 1) - k]) + 1
4.动态规划(次优):求能够测得的最多楼层
dp[i][j]代表i个鸡蛋在j次移动内能测试确定的最多楼层数。
从第一层楼开始更新dp数组。0次移动无法进行测试,初始值dp[i][0]=0。
每多一次移动,可以多进行一次测试。在手中有i-1个鸡蛋的第j-1次移动时已经测出楼高dp[i-1][j-1],一次移动最差情况也可以测出当前楼层的情况,因此逆推出当前能够保证第j次移动得出结果的测试楼层为dp[i-1][j-1]+1。
第j次移动鸡蛋摔碎,可以确定当前层(1层)的结果(碎),剩余i-1个鸡蛋和j-1次移动能得出dp[i-1][j-1]层。
h1 = dp[i][j] = dp[i-1][j-1] + 1;
第j次移动鸡蛋未碎,可以确定前dp[i-1][j-1]+1层的结果(不碎),剩余i个鸡蛋和j-1次移动能得出dp[i][j-1]层。
h2 = dp[i][j] = dp[i-1][j-1] + 1 + dp[i-1][j];
综合结果:
dp = h2 >= max(h1, h2)
5.数学法(最优)
最差情况下(一个鸡蛋)用N次移动可以测得N层楼高,因此可以移动次数x必在[1,N]之间,可以对x进行二分查找。
递归函数fun(x)代表x次移动能够测试的最大楼高。
You are given K eggs, and you have access to a building with N floors from 1 to N.
Each egg is identical in function, and if an egg breaks, you cannot drop it again.
You know that there exists a floor F with 0 <= F <= N such that any egg dropped at a floor higher than F will break, and any egg dropped at or below floor F will not break.
Each move, you may take an egg (if you have an unbroken one) and drop it from any floor X (with 1 <= X <= N).
Your goal is to know with certainty what the value of F is.
What is the minimum number of moves that you need to know with certainty what F is, regardless of the initial value of F?
你将获得 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
import java.util.HashMap;
/**
*
* You are given K eggs, and you have access to a building with N floors from 1 to N.
* Each egg is identical in function, and if an egg breaks, you cannot drop it again.
* You know that there exists a floor F with 0 <= F <= N such that any egg dropped at a floor higher than F will break,
* and any egg dropped at or below floor F will not break.
* Each move, you may take an egg (if you have an unbroken one) and drop it from any floor X (with 1 <= X <= N).
* Your goal is to know with certainty what the value of F is.
* What is the minimum number of moves that you need to know with certainty what F is, regardless of the initial value of F?
* 你将获得 K 个鸡蛋,并可以使用一栋从 1 到 N 共有 N 层楼的建筑。
* 每个蛋的功能都是一样的,如果一个蛋碎了,你就不能再把它掉下去。
* 你知道存在楼层 F ,满足 0 <= F <= N 任何从高于 F 的楼层落下的鸡蛋都会碎,从 F 楼层或比它低的楼层落下的鸡蛋都不会破。
* 每次移动,你可以取一个鸡蛋(如果你有完整的鸡蛋)并把它从任一楼层 X 扔下(满足 1 <= X <= N)。
* 你的目标是确切地知道 F 的值是多少。
* 无论 F 的初始值如何,你确定 F 的值的最小移动次数是多少?
*
*/
public class SuperEggDrop {
//递归(超时)
public int superEggDrop2(int K, int N) {
if(N < 2 || K == 1){
return N;
}
int result = N;
//在手握K个鸡蛋时从第i层楼[0,N]向下丢鸡蛋,会有两种可能:
//1.鸡蛋摔碎,将剩余K-1个鸡蛋移动到楼[0,i-1]进行测递归测试。
//2.鸡蛋没碎,将剩余K个鸡蛋移动到楼[i,N]进行测试,可将i层看成0层,对[0,N-i]进行递归测试。
for(int i = 1; i <= N; i++){
int curMin = Math.max(superEggDrop2(K - 1, i