题目
https://www.lintcode.com/problem/584
有一个n层的建筑。如果一个鸡蛋从第k层及以上落下,它会碎掉。如果从低于这一层的任意层落下,都不会碎。
有m个鸡蛋,用最坏的情况下实验次数最少的方法去找到k, 返回最坏情况下所需的实验次数。
样例
例1:
输入: m = 2, n = 100
输出: 14
例2:
输入: 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,
当只有一个鸡蛋只能从一楼开始往上试,最坏是试完所有楼层,
当只有一层时,只需试一次。
时间复杂度:
指数级别
注意:动态规划版本本身是由递归修改而来文章来源:https://uudwc.com/A/LaXZm
参考答案
本答案的递归版本是参数较大的话,不通过,但是答案没有错,可以用于本地测试;
如果读者使用我的代码,务必注意方法加静态标识符。或者把我的答案静态方法都写为非静态方法,反正要统一文章来源地址https://uudwc.com/A/LaXZm
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
}
}