lintcode 584 · Throwing Eggs II [Medium VIP Dynamic Programming]

topic

https://www.lintcode.com/problem/584

有一个n层的建筑。如果一个鸡蛋从第k层及以上落下,它会碎掉。如果从低于这一层的任意层落下,都不会碎。
有m个鸡蛋,用最坏的情况下实验次数最少的方法去找到k, 返回最坏情况下所需的实验次数。


样例1:

输入: m = 2, n = 100 
输出: 142:

输入: m = 2, n = 36 
输出: 8

Ideas

	有两种方法,本质一样的,第一种递归,会超时但能出来结果,第二种动态规划。
    第一种递归当楼层是10还好,如果是100,程序会运行很久,几分钟也没出来结果,
    还在递归,提交了也不能过,但是对第二种解法有启发性。

    递归方程:
    DropEggs(m,n) = min(1+max(DropEggs(m-1,x-1),DropEggs(m, n-x)),x=1,...n)
    递归出口:
    当m为0或n为0返回0,
    当m为1返回n,
    当n为1返回1。
    解释:
    假设从x楼扔鸡蛋,如果碎了,最低的碎鸡蛋楼层低于x,那么此时剩下m-1个鸡蛋,
    只需要探索x-1层楼,如果没碎,最低的碎鸡蛋楼层在x层的上面,那么此时剩下m个鸡蛋,
    只需探索x层以上的共n-x层,
    由于两种都有可能,题目问的是最坏情况,那么取两种情况下需要扔鸡蛋次数多的情况,
    也就是 max(DropEggs(m-1,x-1),DropEggs(m, n-x))
    那么,如果从x层开始扔,一共需要扔1+max(DropEggs(m-1,x-1),DropEggs(m, n-x))次,
    1代表扔第x层的一次。我们需要知道最优的x,也就是题目问的需要最小的次数,
    那么就需要把x的所有可能都试一试,最终得到答案,x的所有可能是1,…,n。
    当没有鸡蛋或没楼层,次数为0,
    当只有一个鸡蛋只能从一楼开始往上试,最坏是试完所有楼层,
    当只有一层时,只需试一次。
    时间复杂度:
    指数级别

Note: The dynamic programming version itself is modified from recursion

Reference answer

The recursive version of this answer will fail if the parameters are large, but the answer is correct and can be used for local testing;
if readers use my code, be sure to add static identifiers to the method. Or write all the static methods in my answer as non-static methods. Anyway, we need to unify them.

public class Solution {
    
    
    /**
     * @param m: the number of eggs
     * @param n: the number of floors
     * @return: the number of drops in the worst case
     */
    public static int dropEggs2(int m, int n) {
    
    
          //return (int)f1(m,n);  //递归,超时了
        return dp(m,n);  //动态规划
    }

    public static int dp(int m,int n){
    
    
        int[][] dp = new int[m+1][n+1];
        //填充第一列
        for (int i = 1; i <=m ; i++) {
    
    
            dp[i][0]=0;
            dp[i][1]=1;
        }

        for (int j = 1; j <=n ; j++) {
    
     //填充第二行
            dp[1][j] =j;
        }

        for (int i = 2; i <=m ; i++) {
    
    
            for (int j = 2; j <=n ; j++) {
    
    
                dp[i][j] =Integer.MAX_VALUE;
                for (int k = 1; k <j ; k++) {
    
    
                    int tmp = 1+Math.max(dp[i-1][k-1],dp[i][j-k]);
                    dp[i][j]=Math.min(dp[i][j],tmp);
                }
            }
        }

        return dp[m][n];
    }

    //递归
    public static long f1(int m,int n){
    
    
        /*
        有两种方法,本质一样的,第一种递归,会超时但能出来结果,第二种动态规划。
        第一种递归当楼层是10还好,如果是100,程序会运行很久,几分钟也没出来结果,
        还在递归,提交了也不能过,但是对第二种解法有启发性。

        递归方程:
        DropEggs(m,n) = min(1+max(DropEggs(m-1,x-1),DropEggs(m, n-x)),x=1,...n)
        递归出口:
        当m为0或n为0返回0,
        当m为1返回n,
        当n为1返回1。
        解释:
        假设从x楼扔鸡蛋,如果碎了,最低的碎鸡蛋楼层低于x,那么此时剩下m-1个鸡蛋,
        只需要探索x-1层楼,如果没碎,最低的碎鸡蛋楼层在x层的上面,那么此时剩下m个鸡蛋,
        只需探索x层以上的共n-x层,
        由于两种都有可能,题目问的是最坏情况,那么取两种情况下需要扔鸡蛋次数多的情况,
        也就是 max(DropEggs(m-1,x-1),DropEggs(m, n-x))
        那么,如果从x层开始扔,一共需要扔1+max(DropEggs(m-1,x-1),DropEggs(m, n-x))次,
        1代表扔第x层的一次。我们需要知道最优的x,也就是题目问的需要最小的次数,
        那么就需要把x的所有可能都试一试,最终得到答案,x的所有可能是1,…,n。
        当没有鸡蛋或没楼层,次数为0,
        当只有一个鸡蛋只能从一楼开始往上试,最坏是试完所有楼层,
        当只有一层时,只需试一次。
        时间复杂度:
        指数级别
         */
        if(m ==0 || n==0) return 0;
        if(m ==1) return n;
        if(n==1) return 1;
        long ans = Long.MAX_VALUE;
        for (int i = 2; i <n+1 ; i++) {
    
    
            long tmp = 1+Math.max(dropEggs2(m-1,i-1),dropEggs2(m,n-i));
            if(ans > tmp)
                ans = tmp;
        }

        return ans;
    }

}

Locally tested code

public class LC584 {
    
    

    public static int dropEggs2(int m, int n) {
    
    
        //return (int)f1(m,n);  //递归,超时了
        return dp(m,n);  //动态规划
    }

    public static int dp(int m,int n){
    
    
        int[][] dp = new int[m+1][n+1];
        //填充第一列
        for (int i = 1; i <=m ; i++) {
    
    
            dp[i][0]=0;
            dp[i][1]=1;
        }

        for (int j = 1; j <=n ; j++) {
    
     //填充第二行
            dp[1][j] =j;
        }

        for (int i = 2; i <=m ; i++) {
    
    
            for (int j = 2; j <=n ; j++) {
    
    
                dp[i][j] =Integer.MAX_VALUE;
                for (int k = 1; k <j ; k++) {
    
    
                    int tmp = 1+Math.max(dp[i-1][k-1],dp[i][j-k]);
                    dp[i][j]=Math.min(dp[i][j],tmp);
                }
            }
        }

        return dp[m][n];
    }

    //递归
    public static long f1(int m,int n){
    
    
        /*
        有两种方法,本质一样的,第一种递归,会超时但能出来结果,第二种动态规划。
        第一种递归当楼层是10还好,如果是100,程序会运行很久,几分钟也没出来结果,
        还在递归,提交了也不能过,但是对第二种解法有启发性。

        递归方程:
        DropEggs(m,n) = min(1+max(DropEggs(m-1,x-1),DropEggs(m, n-x)),x=1,...n)
        递归出口:
        当m为0或n为0返回0,
        当m为1返回n,
        当n为1返回1。
        解释:
        假设从x楼扔鸡蛋,如果碎了,最低的碎鸡蛋楼层低于x,那么此时剩下m-1个鸡蛋,
        只需要探索x-1层楼,如果没碎,最低的碎鸡蛋楼层在x层的上面,那么此时剩下m个鸡蛋,
        只需探索x层以上的共n-x层,
        由于两种都有可能,题目问的是最坏情况,那么取两种情况下需要扔鸡蛋次数多的情况,
        也就是 max(DropEggs(m-1,x-1),DropEggs(m, n-x))
        那么,如果从x层开始扔,一共需要扔1+max(DropEggs(m-1,x-1),DropEggs(m, n-x))次,
        1代表扔第x层的一次。我们需要知道最优的x,也就是题目问的需要最小的次数,
        那么就需要把x的所有可能都试一试,最终得到答案,x的所有可能是1,…,n。
        当没有鸡蛋或没楼层,次数为0,
        当只有一个鸡蛋只能从一楼开始往上试,最坏是试完所有楼层,
        当只有一层时,只需试一次。
        时间复杂度:
        指数级别
         */
        if(m ==0 || n==0) return 0;
        if(m ==1) return n;
        if(n==1) return 1;
        long ans = Long.MAX_VALUE;
        for (int i = 2; i <n+1 ; i++) {
    
    
            long tmp = 1+Math.max(dropEggs2(m-1,i-1),dropEggs2(m,n-i));
            if(ans > tmp)
                ans = tmp;
        }

        return ans;
    }


    public static void main(String[] args) {
    
    
        System.out.println(dropEggs2(2,36)); //8
        System.out.println(dropEggs2(2,100)); //14

    }
}

Guess you like

Origin blog.csdn.net/weixin_37991016/article/details/132866925