题目描述
原题链接: 2312 卖木头块
解题思路
- 每次切割只能完全横切或者竖切,即切成更矮或者更窄的木块;
- 每次切割后剩余的两部分木块都是更小规模的同类问题,典型的可以用动态规划求解的问题;
- 具体到单个问题,高x宽y的木块能卖的最多钱数有三种情况:
- prices数组中正好有对应宽高的价格,不用切割直接卖;
- 上一刀横切,尝试找到使得[i, y]和[x - i, y]两个矮木块能卖钱数之和最大的i值, 即 $ MAX_{i=1}^{x/2} (dp[i][y] + dp[x - i][y]) $;
- 上一刀竖切,尝试找到使得[x, i]与[x, y - i]两个窄木块能卖钱数之和最大的i值, 即 $ MAX_{i=1}^{y/2} (dp[x][i] + dp[x][y - i]) $。
- 根据这三个情况进行记忆化递归处理规模由大到小的问题;或者从小到大按照状态转移方程求得DP数组值。
解题代码
-
求解规模由大到小, 记忆化递归的解法:
/** * 如果原始prices数组值用Map做哈希表,耗时会在500ms级别 * 执行用时: 79 ms , 在所有 Java 提交中击败了 41.18% 的用户 * 内存消耗: 49.92 MB , 在所有 Java 提交中击败了 76.47% 的用户 */ public long sellingWood(int m, int n, int[][] prices) { long[][] fullPrice = new long[m + 1][n + 1]; for (int[] p : prices) { fullPrice[p[0]][p[1]] = p[2]; } long[][] memo = new long[m + 1][n + 1]; for (int i = 0; i <= m; i++) { Arrays.fill(memo[i], -1); } return process(m, n, fullPrice, memo); } private long process(int height, int width, long[][] fullPrice, long[][] memo) { if (memo[height][width] != -1) { return memo[height][width]; } long ans = fullPrice[height][width]; for (int i = 1; i <= (height >> 1); i++) { ans = Math.max(ans, process(i, width, fullPrice, memo) + process(height - i, width, fullPrice, memo)); } for (int i = 1; i <= (width >> 1); i++) { ans = Math.max(ans, process(height, i, fullPrice, memo) + process(height, width - i, fullPrice, memo)); } memo[height][width] = ans; return ans; }
-
求解规模由小到大, 用状态转移方式求DP数组的解法:
/** * 二维动态规划 * 执行用时: 20 ms , 在所有 Java 提交中击败了 100.00% 的用户 * 内存消耗: 50.77 MB , 在所有 Java 提交中击败了 23.53% 的用户 */ public long sellingWood(int m, int n, int[][] prices) { // dp[x][y]表示高为x宽为y的木块能卖的钱数最大值, prices包含的整块卖的金额是初始值 long[][] dp = new long[m + 1][n + 1]; for (int[] p : prices) { dp[p[0]][p[1]] = p[2]; } for (int i = 1; i <= m; i++) { for (int j = 1; j <= n; j++) { long max = dp[i][j];// 用临时变量而不是直接在第三层for循环内部直接更新dp[i][j],性能稍好些(直接更新的耗时是26ms) for (int k = 1; k <= (i >> 1); k++) { max = Math.max(max, dp[k][j] + dp[i - k][j]); } for (int k = 1; k <= (j >> 1); k++) { max = Math.max(max, dp[i][k] + dp[i][j - k]); } dp[i][j] = max; } } return dp[m][n]; }