Minimum Cost to Merge Stones
Solve this ProblemTest Case 1:
Test Case 2:
Test Case 3:
Constraints
- ◆
1 ≤ stones.length ≤ 8 - ◆
2 ≤ k ≤ stones.length - ◆
1 ≤ stones[i] ≤ 100
Try the Dry Run
Don't just read the solution — watch it execute, one step at a time.
Approach & Solutions
Brute Force — Recursive Without Memoization
BruteLook at a contiguous range of piles as its own smaller version of the same problem: pick where to split the range into two pieces, solve each piece independently and add their costs, and try every split point spaced (k−1) piles apart (any other spacing could never line up with "merge exactly k at a time"). Once a range's own merges are done, check whether the piles now sitting in it can be fused into one final pile in a single k-way move — that's only possible when the range's pile count minus one is itself a multiple of (k−1), which is exactly the same reachability rule that decides whether the whole problem is solvable at all. Recursing this way is correct, but the same range gets re-split and re-solved from scratch every time a different outer split happens to pass through it.
O(exponential)O(n)1class Solution {
2 private int[] stones;
3 private int k;
4 private int[] prefix;
5
6 public int mergeStones(int[] stones, int k) {
7 this.stones = stones;
8 this.k = k;
9 int n = stones.length;
10 if ((n - 1) % (k - 1) != 0) return -1;
11 prefix = new int[n + 1];
12 for (int i = 0; i < n; i++) prefix[i + 1] = prefix[i] + stones[i];
13 return solve(0, n - 1);
14 }
15
16 private int solve(int i, int j) {
17 if (j - i + 1 < k) return 0;
18 int best = Integer.MAX_VALUE;
19 for (int m = i; m < j; m += (k - 1)) {
20 int cost = solve(i, m) + solve(m + 1, j);
21 if (cost < best) best = cost;
22 }
23 if ((j - i) % (k - 1) == 0) {
24 best += prefix[j + 1] - prefix[i];
25 }
26 return best;
27 }
28}Optimal — Bottom-Up Interval DP
OptimalBuild the same recursion bottom-up instead of top-down, filling dp[i][j] — the minimum cost to do every merge that's possible within piles i..j — for shorter ranges before longer ones, so every dp[i][m] and dp[m+1][j] a split needs is already sitting in the table. Each entry still tries every split point (k−1) apart and still adds the range's full stone sum whenever that range can collapse to one pile, exactly like the recursive version — the only difference is that each range's answer gets computed exactly once and reused by every later range that needs it, turning the repeated work into a table lookup.
O(n³ / k)O(n²)1class Solution {
2 public int mergeStones(int[] stones, int k) {
3 int n = stones.length;
4 if ((n - 1) % (k - 1) != 0) return -1;
5 int[] prefix = new int[n + 1];
6 for (int i = 0; i < n; i++) prefix[i + 1] = prefix[i] + stones[i];
7 int[][] dp = new int[n][n];
8 for (int len = k; len <= n; len++) {
9 for (int i = 0; i + len - 1 < n; i++) {
10 int j = i + len - 1;
11 dp[i][j] = Integer.MAX_VALUE;
12 for (int m = i; m < j; m += (k - 1)) {
13 int cost = dp[i][m] + dp[m + 1][j];
14 if (cost < dp[i][j]) dp[i][j] = cost;
15 }
16 if ((len - 1) % (k - 1) == 0) {
17 dp[i][j] += prefix[j + 1] - prefix[i];
18 }
19 }
20 }
21 return dp[0][n - 1];
22 }
23}