ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

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

2021-03-05 16:30:41  阅读:178  来源: 互联网

标签:掉落 int 鸡蛋 面试 算法 楼层 复杂度 dp left


问题描述

在这里插入图片描述

方法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];
    }
};
复杂度分析:
  • 时间复杂度:O(N2K),三层for循环,每层循环都是线性的。
  • 空间复杂度:O(NK),表格大小。
方法2——动态规划+二分

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

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

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

  • dp[k-1][j-1]:根据语义,k增大的时候,楼层大小越大,它的值就越大;
  • dp[i-k][j]:根据语义,k增大的时候,楼层大小越小,它的值就越小。

可以得出一个是单调不减的(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];
    }
};
复杂度分析:
  • 时间复杂度:O(NK \log N)O(NKlogN),其中一层循环变成二分查找,复杂度成为对数;
  • 空间复杂度:O(NK)O(NK),表格的大小。

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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有