编程语言
首页 > 编程语言> > 算法面试难题-1-鸡蛋掉落问题

算法面试难题-1-鸡蛋掉落问题

作者:互联网

问题描述

在这里插入图片描述

方法1——动态规划(超时)
class Solution {
public:
    int superEggDrop(int K, int N) {
        const int INF=0x3f3f3f3f;
        //dp[i][j]:一共有i层楼梯的情况下,使用j个鸡蛋的最少实验的次数
        vector<vector<int>>dp(N+1,vector<int>(K+1,0));

        //初始化
        for(int i=0;i<=N;i++){
            for(int j=0;j<=K;j++){
                dp[i][j]=INF;
            }
        }

        //第0行:楼层为0的时候,不管鸡蛋个数多少,都测试不出鸡蛋的F值,故全为0
        for(int j=0;j<=K;j++){
            dp[0][j]=0;
        }

        //第1行:楼层为1的时候,0个鸡蛋的时候,仍0次,1个以及1个鸡蛋以上只需仍1次
        dp[1][0]=0;
        for(int j=1;j<=K;j++){
            dp[1][j]=1;
        }
        
        //第0列:鸡蛋个数为0的时候,不管楼层为多少,也测试不出鸡蛋的F值,故全为0
        //第1列:鸡蛋个数为1的时候,这是一种极端情况,要测试出F值,最少次数就等于楼层高度
        for(int i=0;i<=N;i++){
            dp[i][0]=0;
            dp[i][1]=i;
        }

        //从第2行,第2列开始填表
        for(int i=2;i<=N;i++){
            for(int j=2;j<=K;j++){
                for(int k=1;k<=i;k++){
                    //碎了,就需要往低层继续扔:层数少1,鸡蛋也少1
                    //不碎,就需要往高楼继续仍:层数是当前层到最高层的距离差,鸡蛋数量不少
                    //两种情况都做了一次尝试,所以加1
                    dp[i][j]=min(dp[i][j],max(dp[k-1][j-1],dp[i-k][j])+1);
                }
            }
        }

        return dp[N][K];
    }
};
复杂度分析:
方法2——动态规划+二分

这里需要盯着[状态转移方程]使劲看:

	dp[i][j]=min(max(dp[k-1][j-1],dp[i-k][j])+1);

[状态转移方程]里最外层的变量是k,它枚举了扔下鸡蛋的楼层的高度,这里它是自变量,将其余的i和j视为常数:

可以得出一个是单调不减的(dp[k-1][j-1],下面红点),一个是单调不增的(dp[i-k][j],下面绿星),并且它们的值都是整数。
我们使用了一组数据,制作成图标(每次取数据都取最后一行最后一列的那个单元格计算的数据)。
情况1:最低点只有1个点
在这里插入图片描述
情况2:最低点是若干个重合的点
在这里插入图片描述
情况3:最低点不重合,但是两边的值一样
在这里插入图片描述
从图上可以看出:二者的较大值的最小点在它们交汇的地方。那么有没有可能不交汇,当前有可能(上面第3张图),二者较大值的最小者一定出现在画成曲线段交点的两侧,并且二者的差值不会超过1,也就是如果没有重合的点,两边的最大值是一样的(从图上看出来的,没有严格证明),因此取左侧和右侧两点中的一点都可以,不失一般性,可以取左边的那个点的k。
也就是找到使得dp[i-k][j]<=dp[k-i][j-1]最大的那个k值即可。这里使用二分查找算法。关键在于dp[i-k][j]>dp[k-i][j-1]的时候,k一定不少我们要找的,根据这一点写出二分的代码。

class Solution {
public:
    int superEggDrop(int K, int N) {
        const int INF=0x3f3f3f3f;
        //dp[i][j]:一共有i层楼梯的情况下,使用j个鸡蛋的最少实验的次数
        vector<vector<int>>dp(N+1,vector<int>(K+1,0));

        //初始化
        for(int i=0;i<=N;i++){
            for(int j=0;j<=K;j++){
                dp[i][j]=INF;
            }
        }

        //第0行:楼层为0的时候,不管鸡蛋个数多少,都测试不出鸡蛋的F值,故全为0
        for(int j=0;j<=K;j++){
            dp[0][j]=0;
        }

        //第1行:楼层为1的时候,0个鸡蛋的时候,仍0次,1个以及1个鸡蛋以上只需仍1次
        dp[1][0]=0;
        for(int j=1;j<=K;j++){
            dp[1][j]=1;
        }
        
        //第0列:鸡蛋个数为0的时候,不管楼层为多少,也测试不出鸡蛋的F值,故全为0
        //第1列:鸡蛋个数为1的时候,这是一种极端情况,要测试出F值,最少次数就等于楼层高度
        for(int i=0;i<=N;i++){
            dp[i][0]=0;
            dp[i][1]=i;
        }

        //从第2行,第2列开始填表
        for(int i=2;i<=N;i++){
            for(int j=2;j<=K;j++){
                //在区间[1,i]里确定一个最优值
                int left=1;
                int right=i;
                while(left<right){
                    //找dp[k-1][j-1]<=dp[i-mid][j]的最大值k
                    int mid=left+(right-left+1)/2;
                    
                    int breadCount=dp[mid-1][j-1];
                    int noBreakCount=dp[i-mid][j];
                    if(breadCount>noBreakCount){
                        //严格大于的时候一定不是解,此时mid一定不是解
                        //下一轮搜索区间是[left,mid-1]
                        right=mid-1;
                    }else {
                        //这个区间一定是上一个区间的反面,即[mid,right]
                        //注意这个时候取中间数要上取整,int mid=left+(right-left+1)/2;
                        left=mid;
                    }
                }
                //left这个下标就是最优的k值,把它代入转移方程
                dp[i][j]=max(dp[left-1][j-1],dp[i-left][j])+1;
            }
        }

        return dp[N][K];
    }
};
复杂度分析:

标签:掉落,int,鸡蛋,面试,算法,楼层,复杂度,dp,left
来源: https://blog.csdn.net/qq_41476257/article/details/114399340