https://leetcode.com/problems/super-egg-drop/
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
?
Example 1:
Input: K = 1, N = 2
Output: 2
Explanation:
Drop the egg from floor 1. If it breaks, we know with certainty that F = 0.
Otherwise, drop the egg from floor 2. If it breaks, we know with certainty that F = 1.
If it didn't break, then we know with certainty F = 2.
Hence, we needed 2 moves in the worst case to know what F is with certainty.
Note:
-
1 <= K <= 100
-
1 <= N <= 10000
题目分析:找出鸡蛋从多少层楼扔下去刚好不碎的临界值 f 需要操作最少需要多少次,如果不考虑可用鸡蛋数目,最好的解决方法就是二分法
算法思路:正如题目分析中的描述,当我们只有1个鸡蛋的时候,要找出临界值,最少的次数一定是当前楼层数,因为需要一层一层的增加,如果不是,鸡蛋碎了,我们就没有鸡蛋可以测试找出 f 了,故当k==1时,return n(要考虑最坏的情况),当楼层n==0时,就不需要下一步测试了,return 0,这就是两个base case;然后针对任意状态,需要针对的可控变量就是鸡蛋数 k 和楼层数 n,定义状态函数dp(k,n)表示当前有k个鸡蛋,需要测试的楼层数是n,最少的测试次数。由于有n层楼,使用穷举遍历,找出在 k 个鸡蛋下的最少次数。接下来找出状态转移,当在 i 层扔鸡蛋时,鸡蛋最后只有两种状态,碎与不碎,碎了则下一步状态函数为dp(k-1,i-1),不碎则下一步状态函数为dp(k,n-i),所以针对第 i 层扔鸡蛋,最坏次数是max(dp(k-1,i-1),dp(k,n-i)) +1(因为理论分析要保证所有情况都有,不是实际做实验,所以把最坏情况考虑进来,保证一定可以找到那个 f ),1是第 i 层的1次实验。
为了大量重复计算,使用记忆化搜索(备忘录),降低计算量,第一段代码就是这个思路,但是超时了。
第二段代码优化穷举遍历,但看dp(k-1,i-1)是关于i的增函数,dp(k,n-i)是关于i的减函数,随意两者存在一个交点,但是交点所对应的k和n不一定是整数,所以对于跳出循环的lo和hi分别检测下,找出最小值即可。
//O(k*n^2) O(k*n)
//Time Limit Exceeded
class Solution {
public:
int superEggDrop(int K, int N) {
memo=vector<vector<int>>(K+1,vector<int>(N+1,-1));
return dp(K,N);
}
private:
vector<vector<int>> memo;
//函数状态定义,鸡蛋数k,需要测试的楼层数n
int dp(int k,int n){
if(k==1) return n;
if(n==0) return 0;
if(memo[k][n]!=-1)
return memo[k][n];
//穷举所有可能
int res=INT_MAX;
for(int i=1;i<n+1;i++){
res=min(res,max(dp(k-1,i-1),dp(k,n-i))+1);
}
memo[k][n]=res;
return res;
}
};
//O(k*n*logn) O(k*n)
//Runtime: 36 ms, faster than 54.28% of C++ online submissions for Super Egg Drop.
//Memory Usage: 32.3 MB, less than 14.29% of C++ online submissions for Super Egg Drop.
class Solution {
public:
int superEggDrop(int K, int N) {
memo=vector<vector<int>>(K+1,vector<int>(N+1,-1));
return dp(K,N);
}
private:
vector<vector<int>> memo;
//函数状态定义,鸡蛋数k,需要测试的楼层数n
int dp(int k,int n){
if(k==1) return n;
if(n==0) return 0;
if(memo[k][n]!=-1)
return memo[k][n];
//二分法
int lo=1,hi=n;
while(lo+1<hi){
int mid=lo+(hi-lo)/2;
int t1=dp(k-1,mid-1);
int t2=dp(k,n-mid);
if(t1<t2){
lo=mid;
}else if(t1>t2){
hi=mid;
}else{
lo=mid;
hi=mid;
}
}
int res=1+min(max(dp(k-1,lo-1),dp(k,n-lo)),max(dp(k-1,hi-1),dp(k,n-hi)));
memo[k][n]=res;
return res;
}
};
还有时间复杂度更低的算法,待续。。。