887. Super Egg Drop

27 篇文章 0 订阅

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. 1 <= K <= 100

  2. 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;
    }
};

还有时间复杂度更低的算法,待续。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值