import java.util.*; /** * NC87 丢棋子问题 * @author d3y1 */ public class Solution { /** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * * 返回最差情况下扔棋子的最小次数 * @param n int整型 楼层数 * @param k int整型 棋子数 * @return int整型 */ public int solve (int n, int k) { // return solution1(n, k); return solution2(n, k); } /** * 动态规划: 二维数组 * * dp[i][j]表示i个棋子扔j次能测的最多楼层数 * * 考虑第i个棋子在最优的楼层扔下去, 会有两种情况: * (1) 碎了, 那么上面就不用测了, 下面能测的楼层数是 dp[i-1][j-1] * (2) 没碎, 那么下面就不用测了, 上面能测的楼层数是 dp[i][j-1] * (3) 第i个棋子扔掉的那一层也能测 * * 考虑最差的情形, 可以得到转移式: * dp[i][j] = dp[i][j-1] + dp[i-1][j-1] + 1 * * 棋子数i\次数j 0 1 2 3 4 5 6 7 8 9 10 * 1 0 1 2 3 4 5 6 7 8 9 10 * 2 0 1 3 6 10 15 21 28 36 45 55 * 3 0 1 3 7 14 25 41 63 92 129 175 * 4 0 1 3 7 15 30 56 98 162 255 385 * 5 0 1 3 7 15 31 62 119 218 381 637 * 从上到下 从左往右(按列) * * @param n * @param k * @return */ private int solution1(int n, int k){ if(n == 0){ return 0; } if(k == 1){ return n; } // 对n楼层进行二分测试(最优) 需要测试次数log2(n)+1 int times = (int)(Math.log(n)/Math.log(2))+1; // 如果棋子数k足够大, 二分的扔(二分测试)即可 if(k > times) { return times; } int[][] dp = new int[k+1][n+1]; // 0个棋子 for(int i=0; i<=n; i++) { dp[0][i] = 0; } // 扔0次 for(int i=0; i<=k; i++) { dp[i][0] = 0; } int j = 0; while (dp[k][j] < n) { j++; for(int i = 1; i <= k; i++){ dp[i][j] = dp[i][j-1] + dp[i-1][j-1] + 1; } } return j; } /** * 动态规划: 一维数组 (空间压缩) * * dp[i]表示i个棋子扔j次能测的最多楼层数 * * 考虑第i个棋子在最优的楼层扔下去, 会有两种情况: * (1) 碎了, 那么上面就不用测了, 下面能测的楼层数是 dp[i-1][j-1] * (2) 没碎, 那么下面就不用测了, 上面能测的楼层数是 dp[i][j-1] * (3) 第i个棋子扔掉的那一层也能测 * * 考虑最差的情形, 可以得到转移式: * dp[i][j] = dp[i][j-1] + dp[i-1][j-1] + 1 * * 空间压缩: * dp[i] = dp[i] + dp_i_1 + 1 * * 棋子数i\次数j 0 1 2 3 4 5 6 7 8 9 10 * 1 0 1 2 3 4 5 6 7 8 9 10 * 2 0 1 3 6 10 15 21 28 36 45 55 * 3 0 1 3 7 14 25 41 63 92 129 175 * 4 0 1 3 7 15 30 56 98 162 255 385 * 5 0 1 3 7 15 31 62 119 218 381 637 * 从上到下 从左往右(按列) * * @param n * @param k * @return */ private int solution2(int n, int k){ if(n == 0){ return 0; } if(k == 1){ return n; } // 对n楼层进行二分测试(最优) 需要测试次数log2(n)+1 int times = (int)(Math.log(n)/Math.log(2))+1; // 如果棋子数k足够大, 二分的扔(二分测试)即可 if(k > times) { return times; } int[] dp = new int[k+1]; // 0个棋子 dp[0] = 0; int j = 0; while (dp[k] < n) { j++; // dp[i-1][j-1] int dp_i_1 = 0; for(int i = 1; i <= k; i++){ int tmp = dp[i]; dp[i] = dp[i] + dp_i_1 + 1; dp_i_1 = tmp; } } return j; } }