LeetCode刷题笔记 887. 鸡蛋掉落 【动态规划】

法一 巧解

887. 鸡蛋掉落

https://leetcode-cn.com/problems/super-egg-drop/solution/ji-dan-diao-luo-xiang-jie-by-shellbye/

其实我们可以换一个思路来想:“求k个鸡蛋在m步内可以测出多少层”。我们令dp[k][m]表示k个鸡蛋在m步内可以测出的最多的层数,那么当我们在第X层扔鸡蛋的时候,就有两种情况:

  1. 鸡蛋碎了,我们少了一颗鸡蛋,也用掉了一步,此时测出 N − X + d p [ k − 1 ] [ m − 1 ] N - X + dp[k-1][m-1] NX+dp[k1][m1] 层,X和它上面的N-X层已经通过这次扔鸡蛋确定大于F;
  2. 鸡蛋没碎,鸡蛋的数量没有变,但是用掉了一步,剩余 X + d p [ k ] [ m − 1 ] X + dp[k][m-1] X+dp[k][m1] ,X层及其以下已经通过这次扔鸡蛋确定不会大于F;

也就是说,我们每一次扔鸡蛋,不仅仅确定了下一次扔鸡蛋的楼层的方向,也确定了另一半楼层与F的大小关系,所以在下面的关键代码中,使用的不再是max,而是加法(这里是重点)。有人问到为什么是相加,其实这里有一个惯性思维的误区,诸多解法中,往往求max的思路是“两种方式中较大的那一个结果”,其实这里的相加,不是鸡蛋碎了和没碎两种情况的相加,而是“本次扔之后可能测出来的层数 + 本次扔之前已经测出来的层数”。

这里的m是扔的次数

class Solution {
public:
int superEggDrop(int K, int N) {
    // m 最多不会超过 N 次(线性扫描)
    int dp[K + 1][N + 1];
    // base case:
    // dp[..][0] = 0
    // dp[0][..] = 0

    for(int i=0;i<K+1;i++) dp[i][0]=0;
    for(int j=0;j<N+1;j++) dp[0][j]=0;
    int m = 0;
    while (dp[K][m] < N) {
        m++;
        for (int k = 1; k <= K; k++)
            dp[k][m] = dp[k][m - 1] + dp[k - 1][m - 1] + 1;
    }
    return m;
}
};

作者:wat-2
链接:https://leetcode-cn.com/problems/super-egg-drop/solution/zhi-xing-yong-shi-0-ms-zai-suo-you-java-ti-jia-121/

    /
     * 鸡蛋掉落,鹰蛋(Leetcode 887):(经典dp)
     * 有 K 个鸡蛋,有 N 层楼,用最少的操作次数 F 检查出鸡蛋的质量。
     *
     * 思路:
     * 本题应该逆向思维,若你有 K 个鸡蛋,你最多操作 F 次,求 N 最大值。
     *
     * dp[k][f] = dp[k][f-1] + dp[k-1][f-1] + 1;
     * 解释:
     * 0.dp[k][f]:如果你还剩 k 个蛋,且只能操作 f 次了,所能确定的楼层。
     * 1.dp[k][f-1]:蛋没碎,因此该部分决定了所操作楼层的上面所能容纳的楼层最大值
     * 2.dp[k-1][f-1]:蛋碎了,因此该部分决定了所操作楼层的下面所能容纳的楼层最大值
     * 又因为第 f 次操作结果只和第 f-1 次操作结果相关,因此可以只用一维数组。
     *
     * 时复:O(K*根号(N))
     /

在这里插入图片描述
在这里插入图片描述

class Solution {
public:
    int superEggDrop(int K, int N) {
        vector<int> dp(K+1);
        int ans=0;
        while(dp[K]<N){
            for(int i=K;i>0;i--) // 从后往前计算
                dp[i]=dp[i]+dp[i-1]+1;
            ans++;
        }
        return ans;
    }
};

流程打印

class Solution {
public:
    int superEggDrop(int K, int N) {
        vector<int> dp(K+1);
        int ans=0;
        while(dp[K]<N){
            for(int i=K;i>0;i--){ // 从后往前计算
                cout<<"i="<<i<<"   ";
                dp[i]=dp[i]+dp[i-1]+1;
                cout<<"dp["<<i<<"]="<<dp[i]<<"   ";
            }
            cout<<endl;
            ans++;
            cout<<"ans="<<ans<<endl;
        }
        return ans;
    }
};

a n s = d p [ 1 ] ans=dp[1] ans=dp[1]

输入
2
100

输出
14

i=2   dp[2]=1   i=1   dp[1]=1   
ans=1
i=2   dp[2]=3   i=1   dp[1]=2   
ans=2
i=2   dp[2]=6   i=1   dp[1]=3   
ans=3
i=2   dp[2]=10   i=1   dp[1]=4   
ans=4
i=2   dp[2]=15   i=1   dp[1]=5   
ans=5
i=2   dp[2]=21   i=1   dp[1]=6   
ans=6
i=2   dp[2]=28   i=1   dp[1]=7   
ans=7
i=2   dp[2]=36   i=1   dp[1]=8   
ans=8
i=2   dp[2]=45   i=1   dp[1]=9   
ans=9
i=2   dp[2]=55   i=1   dp[1]=10   
ans=10
i=2   dp[2]=66   i=1   dp[1]=11   
ans=11
i=2   dp[2]=78   i=1   dp[1]=12   
ans=12
i=2   dp[2]=91   i=1   dp[1]=13   
ans=13
i=2   dp[2]=105   i=1   dp[1]=14   
ans=14

法二 一般思路

暴力(超时)

class Solution {
public:
    int recursive(int K,int N){
        if(K==1||N==0||N==1) return N;
        int minimum=N;
        for(int i=1;i<=N;i++){
            int tMin=max(recursive(K-1,i-1),recursive(K,N-i))+1;
            minimum=min(minimum,tMin);
        }
        return minimum;
    }
    int superEggDrop(int K, int N) {
        return recursive(K,N);
    }
};

DP table(继续超时)

class Solution {
public:
    int superEggDrop(int K, int N) {
        int dp[K+1][N+1];
        for(int i=1;i<=N;i++){
            dp[1][i]=i;  // 只有一个鸡蛋
            dp[0][i]=0;  // 没有鸡蛋
        }
        for(int i=1;i<=K;i++){
            dp[i][0]=0;  // 第0层
        }
        for(int k=2;k<=K;k++){  // 从两个鸡蛋开始
            for(int n=1;n<=N;n++){
                int tMinDrop=N;
                for(int x=1;x<=n;x++){
                    tMinDrop=min(tMinDrop,1+max(dp[k-1][x-1],dp[k][n-x]));
                }
                dp[k][n]=tMinDrop;
            }
        }
        return dp[K][N];
    }
};

二分法

C++ sol (DP) 详解
这里n是楼层

class Solution {
public:
    int superEggDrop(int K, int N) {
        vector<vector<int>> dp(K+1, vector<int>(N+1, 0));
        return helper(dp, K, N);
    }

    int helper(vector<vector<int>>& dp, int K, int N) {
        if (1 == K) return N;
        if (0 == N||N == 1) return N;
        if (dp[K][N]!=0) return dp[K][N];

        int res = INT_MAX;
        int l=0, r=N; // broken 单调递增(i-1: 随i增,单调递增);notBroken 单调递减(N-i: 随i增,单调递减)。因此这里可以用二分来求相交点
        while (l <= r) {
            int mid = (l+r)/2;
            int broken = helper(dp, K-1, mid-1);
            int notBroken = helper(dp, K, N-mid);

            if (broken > notBroken) {
                r=mid-1;
                res = min(res, broken+1);
            } else {
                l=mid+1;
                res = min(res, notBroken+1);
            }
        }

        dp[K][N] = res;
        return res;
    }
};

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值