zoukankan      html  css  js  c++  java
  • 经典动规鸡蛋掉落问题

    最近在复习动态规划,然后就想看一下自己的成果,就去力扣翻了下这道题。比较经典
    贴个力扣链接:https://leetcode-cn.com/problems/super-egg-drop/
    还记得刚开始看的时候,看的一知半解,现在又看这道题,来写一下自己的思路和心得
    首先,读题,我个人倾向于读完题,然后去看实例加深一下理解。当只有一个鸡蛋的时候,那要试出来肯定得一层一层爬,然后最差的情况就是,爬到了顶楼鸡蛋才碎,或者没碎。所以n层楼一个鸡蛋,最少需要n次,这是毫无疑问的。
    然后就是两个蛋的情况了,这个时候我有两个蛋,我可以在中间找个楼层去扔,如果运气差,碎了,那我就只有一个蛋了,唉,这是不是可以回归到一个蛋的情况,比方说我在m层扔碎了,那么我还需要m-1次,总共加起来是m次吧。然后思考一下,我有两个蛋的时候,我经历了什么,先扔碎一个蛋,把问题简化为一个蛋,巧了,这不正好可以用到动规么。dp[k][m]表示k个蛋在m层需要的次数。比方说这个例子里,我在m层扔了蛋,碎掉了,我去取dp[1][m-1] = m-1;是吧,那么dp[2][n] = dp[1][m-1]+1;
    不过这个表达式还是有问题,里面还有m和n,我们需要找到mn的对应关系,不能乱扔是吧,比方说我在五楼扔蛋和九楼扔蛋,结果就不一样。
    我一开始想的比较复杂,想求出这个分割点,然后每次去取,如果求出来了,运行速度估计还是很快的,但是这很不算法。我觉得写算法的人是不要参与这种复杂的脑力的,它只需要让电脑去机械的执行出答案来好了。然后发现,如果我在m层蛋没碎,那么问题是不是可以看成两个蛋,m-n层需要多少次呢。
    好,现在解法很明显了,中间选个点,分情况讨论。

    • 蛋碎了,需要dp[k-1][m]次
    • 蛋没碎,需要dp[k][n-m]次
      初步想法就是,在这里遍历,便利出最短的次数,给dp[k][n]赋值。
      代码实现如下
    public int superEggDrop(int k, int n) {
            if(k==1){
                return n;
            }
            int[][] dp = new int[k+1][n+1];
            for(int i=1;i<n+1;i++){
                dp[1][i] = i;
            }
            for(int i=2;i<k+1;i++){
                for(int j=1;j<n+1;j++){
                    int min = dp[i-1][j];
                    for(int l=2;l<j;l++){
                        min = Math.min(min, Math.max(dp[i-1][l-1]+1, dp[i][j-l]+1));
                    }
                    dp[i][j] = min;
                }
            }
            return dp[k][n];
        }
    

    这里三重循环,拿去力扣跑一下,果然超时了。
    再重新看下代码,唯一能优化的只有这一段

    for(int l=2;l<j;l++){
        min = Math.min(min, Math.max(dp[i-1][l-1]+1, dp[i][j-l]+1));
    }
    

    如果把dp[i][x]看成一个函数的话,他肯定是单调递增的,那么dp[i][j-x]必定单调减。也就是说这俩的交点,正好是我们要的答案。
    但是这函数不是连续的,我们要的点可能不是一个整数,那最小值就是左右两端的点了,直接用二分法去找。在重合点左边的时候dp[i][x]是小于dp[i][j-x]的,那么每次取中点,判断他的状态,如果在左边更新左边的点,反之更新右边的点,最后取到左右点,取出最小值。
    代码如下:

    public int superEggDrop(int k, int n) {
            if(k==1){
                return n;
            }
            int[][] dp = new int[k+1][n+1];
            for(int i=1;i<n+1;i++){
                dp[1][i] = i;
            }
            for(int i=2;i<k+1;i++){
                for(int j=1;j<n+1;j++){
                    int min = dp[i-1][j];
                    int left=2,right=j;
                    while(left<right){
                        int center = (left+right)/2;
                        if(center==left){
                            min = Math.min(min, Math.max(dp[i-1][center-1]+1, dp[i][j-center]+1));
                            min = Math.min(min, Math.max(dp[i-1][right-1]+1, dp[i][j-right]+1));
                            break;
                        }
                        boolean centerB = dp[i-1][center-1]<dp[i][j-center];
                        if(centerB){
                            left = center;
                        }else{
                            right = center;
                        }
                    }
                    dp[i][j] = min;
                }
            }
            return dp[k][n];
        }
    
  • 相关阅读:
    不开心的事
    git push 时 error: RPC failed; HTTP 400 curl 55 Send failure: Connection was reset 问题
    Java多线程相关
    angularJS 级联下拉框
    leetcode260 Single Number III
    -2147483648的绝对值
    git 提交信息模板
    rabbitmq at com.rabbitmq.client.impl.Frame.readFrom(Frame.java:91) ~[amqp-client-5.4.3.jar:5.4.3] 错误
    Unity 切换场景的注意点
    Java位运算
  • 原文地址:https://www.cnblogs.com/afei123/p/15818900.html
Copyright © 2011-2022 走看看