丢棋子问题
原文链接:https://www.cnblogs.com/willwuss/p/12256475.html
题目描述
一座大楼一共有0~N层,地面算第0层,最高一层为第N层。已知棋子从第0层掉落肯定不会摔碎,从第i层掉落可能回摔碎,也可能不会摔碎(1<=i<=N)。给定整数N作为楼层数,再给定整数K作为棋子数,返回如果想找到棋子不会摔碎的最高层数,即使在最差的情况下仍的最少次数。一次只能仍一个棋子。
例子
N=10, K=1.
返回10。因为只有1颗棋子,所以不得不从第一层开始一直试到第十层,最差情况要扔10次。
N=3, K=2.
返回2。现在第2层仍1颗棋子,如果碎了,试第1层;如果没碎,试第3层。
N=105, K=2.
返回14。
暴力解法
** Solution one **
** 时间复杂度 O(N!) **
public int solutionOne(int N, int K){
if ( N<1 || K<1 )
return 0;
return helper1(N, K);
}
private int helper1(int N, int K){
if ( N==0 ) return 0;
if ( K==1 ) return N;
int min = Integer.MAX_VALUE;
for(int i=1; i<=N; ++i){
min = Math.min(min, Math.max( helper1(i-1, K-1), helper1(N-i, K)));
}
return min+1;
}
最优解
代码实现Map建立部分,对应上图的表应该把头左斜着90°看
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
* 返回最差情况下扔棋子的最小次数
* @param n int整型 楼层数
* @param k int整型 棋子数
* @return int整型
*/
public int solve (int n, int k) {
// write code here
if(n < 1 || k < 1) return 0;
//另外还有一个特别重要的优化,我们知道N层楼完全用二分的方式扔logN+1次就直接可以确定哪层楼是会碎的最低层楼
int times = (int)(Math.log(n)/Math.log(2)) + 1;
if(k >= times) return times;
int[] dp = new int[k];
int res = 0;
//构建几个棋子可以扔多少次就能最大判断的楼数Map表,
//map[i][j] == map[i-1][j] + map[i-1][j-1] + 1
while(true){
res ++;
int pre = 0;
for(int i=0; i<k; i++){
int temp = dp[i];
dp[i] = dp[i] + pre + 1;
pre = temp;
if(dp[i] >= n) return res;
}
}
}
}