lintcode 584 · 丢鸡蛋II 【中等 vip 动态规划】

题目

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

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


样例
例1:

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

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

思路

	有两种方法,本质一样的,第一种递归,会超时但能出来结果,第二种动态规划。
    第一种递归当楼层是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,
    当只有一个鸡蛋只能从一楼开始往上试,最坏是试完所有楼层,
    当只有一层时,只需试一次。
    时间复杂度:
    指数级别

注意:动态规划版本本身是由递归修改而来

参考答案

本答案的递归版本是参数较大的话,不通过,但是答案没有错,可以用于本地测试;
如果读者使用我的代码,务必注意方法加静态标识符。或者把我的答案静态方法都写为非静态方法,反正要统一

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;
    }

}

本地测试的代码

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

    }
}

你可能感兴趣的:(动态规划,算法)