Problem: 1000. 合并石头的最低成本
文章目录
- 思路
- 解题方法
- 复杂度
- Code
思路
这道题目的核心在于理解合并石头的过程和寻找最优策略。给定一个数组 stones 表示石堆,以及一个整数 k 表示每次可以合并的石堆数量,目标是找到将所有石堆合并成一个石堆的最小成本,每次合并相邻的 k 个石堆需要消耗的成本为这些石堆中石头的总和。观察题目条件,合并过程具有最优子结构,可以通过动态规划解决。难点在于确定状态转移方程,因为合并操作不是简单的两两合并,而是每次可以合并 k 个连续的石堆,因此需要设计一个二维的动态规划数组来记录区间内石堆合并的最小成本。
解题方法
预处理前缀和:首先计算出 preSum 数组,用于快速获取区间内石堆的总重量,加速后续计算。初始化动态规划数组:由于合并操作的特殊性(每次合并 k 个),我们需要用二维数组 dp[l][r] 来记录从索引 l 到 r 区间内所有石堆合并的最小成本。初始化时不需要特别设置,因为后续会覆盖。状态转移:从长度较小的子区间开始递推到整个区间,对于每个区间 [l, r],枚举其中可以作为最后一次合并操作结束位置的所有 m(步长为 k-1),计算通过 dp[l][m] 和 dp[m+1][r] 合并的成本,并累加区间内的石堆总重量(当 (r-l) % (k-1) == 0 时),取最小值作为 dp[l][r] 的值。检查可行性:在开始动态规划之前,先判断是否能刚好合并成一个石堆,即 (n-1) % (k-1) == 0,若不满足则直接返回 -1,表示无法按规则完成合并。返回结果:最终 dp[0][n-1] 即为所求的最小成本。
复杂度
时间复杂度:
O ( n 3 ) O(n^3) O(n3)。主要消耗在三层循环上,尽管外层循环随着 l 增加而减少迭代次数,但总体仍接近立方级别。
空间复杂度:
O ( n 2 ) O(n^2) O(n2)。需要一个 n × n 的二维数组来存储动态规划的状态。
Code
class Solution {public int mergeStones(int[] stones, int k) {int n = stones.length;if((n - 1) % (k - 1) != 0) {return -1;}int[] preSum = new int[n + 1];for(int i = 1; i <= n; i++) {preSum[i] += stones[i - 1];preSum[i] += preSum[i - 1];}int[][] dp = new int[n][n];for(int l = n - 2, ans; l >= 0; l--) {for(int r = l + 1; r < n; r++) {ans = Integer.MAX_VALUE;for(int m = l; m < r; m += k - 1) {ans = Math.min(ans, dp[l][m] + dp[m + 1][r]);}if((r - l) % (k - 1) == 0) {ans += preSum[r + 1] - preSum[l];}dp[l][r] = ans;}}return dp[0][n - 1];}
}