There are N
piles of stones arranged in a row. The i
-th pile has stones[i]
stones.
A move consists of merging exactly K
consecutive piles into one pile, and the cost of this move is equal to the total number of stones in these K
piles.
Find the minimum cost to merge all piles of stones into one pile. If it is impossible, return -1
.
Example 1:
Input: stones = [3,2,4,1], K = 2 Output: 20 Explanation: We start with [3, 2, 4, 1]. We merge [3, 2] for a cost of 5, and we are left with [5, 4, 1]. We merge [4, 1] for a cost of 5, and we are left with [5, 5]. We merge [5, 5] for a cost of 10, and we are left with [10]. The total cost was 20, and this is the minimum possible.
Example 2:
Input: stones = [3,2,4,1], K = 3 Output: -1 Explanation: After any merge operation, there are 2 piles left, and we can't merge anymore. So the task is impossible.
Example 3:
Input: stones = [3,5,1,2,6], K = 3 Output: 25 Explanation: We start with [3, 5, 1, 2, 6]. We merge [5, 1, 2] for a cost of 8, and we are left with [3, 8, 6]. We merge [3, 8, 6] for a cost of 17, and we are left with [17]. The total cost was 25, and this is the minimum possible.
Note:
1 <= stones.length <= 30
2 <= K <= 30
1 <= stones[i] <= 100
Solution: DP
dp[i][j][k] := min cost to merge subarray i ~ j into k piles
Init: dp[i][j][k] = 0 if i==j and k == 1 else inf
ans: dp[0][n-1][1]
transition:
1. dp[i][j][k] = min{dp[i][m][1] + dp[m+1][j][k-1]} for all i <= m < j
2. dp[i][j][1] = dp[i][j][K] + sum(A[i]~A[j])
Time complexity: O(n^3)
Space complexity: O(n^2*K)
C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
// Running time: 12 ms, 12.1 MB class Solution { public: int mergeStones(vector<int>& stones, int K) { const int n = stones.size(); if ((n - 1) % (K - 1)) return -1; const int kInf = 1e9; vector<int> sums(n + 1); for (int i = 0; i < stones.size(); ++i) sums[i + 1] = sums[i] + stones[i]; // dp[i][j][k] := min cost to merge subarray i~j into k piles. vector<vector<vector<int>>> dp(n, vector<vector<int>>(n, vector<int>(K + 1, kInf))); for (int i = 0; i < n; ++i) dp[i][i][1] = 0; for (int l = 2; l <= n; ++l) // subproblem length for (int i = 0; i <= n - l; ++i) { // start int j = i + l - 1; // end for (int k = 2; k <= K; ++k) // piles for (int m = i; m < j; m += K - 1) // split point dp[i][j][k] = min(dp[i][j][k], dp[i][m][1] + dp[m + 1][j][k - 1]); dp[i][j][1] = dp[i][j][K] + sums[j + 1] - sums[i]; } return dp[0][n - 1][1]; } }; |
C++/top down
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
// Author: Huahua, running time: 12 ms class Solution { public: int mergeStones(vector<int>& stones, int K) { const int n = stones.size(); if ((n - 1) % (K - 1)) return -1; const int kInf = 1e9; vector<int> sums(n + 1); for (int i = 0; i < stones.size(); ++i) sums[i + 1] = sums[i] + stones[i]; vector<vector<vector<int>>> cache(n, vector<vector<int>>(n, vector<int>(K + 1, INT_MAX))); std::function<int(int, int, int)> dp = [&stones, &sums, &cache, kInf, n, K, &dp](int i, int j, int k) { if ((j - i + 1 - k) % (K - 1)) return kInf; if (i == j) return k == 1 ? 0 : kInf; if (cache[i][j][k] != INT_MAX) return cache[i][j][k]; if (k == 1) return cache[i][j][k] = dp(i, j, K) + sums[j + 1] - sums[i]; int ans = kInf; for (int m = i; m < j; m += K - 1) { int l = dp(i, m, 1); if (l >= ans) continue; int r = dp(m + 1, j, k - 1); if (r >= ans) continue; ans = min(ans, l + r); } return cache[i][j][k] = ans; }; return dp(0, n - 1, 1); } }; |
Solution 2: DP
dp[l][i] := min cost to merge [i, i + l) into as less piles as possible. Number of merges will be (l-1) / (K – 1) and
Transition: dp[l][i] = min(dp[m][i] + dp[l – m][i + m]) for 1 <= m < l
if ((l – 1) % (K – 1) == 0) [i, i + l) can be merged into 1 pile, dp[l][i] += sum(A[i:i+l])
Time complexity: O(n^3 / k)
Space complexity: O(n^2)
C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
// Author: Huahua 4ms, 9.6 MB class Solution { public: int mergeStones(vector<int>& stones, int K) { const int n = stones.size(); if ((n - 1) % (K - 1)) return -1; vector<int> sums(n + 1); for (int i = 0; i < stones.size(); ++i) sums[i + 1] = sums[i] + stones[i]; const int kInf = 1e9; // dp[i][j] := min cost to merge subarray A[i] ~ A[j] into (j-i)%(K-1) + 1 piles vector<vector<int>> dp(n, vector<int>(n, kInf)); for (int i = 0; i < n; ++i) dp[i][i] = 0; for (int l = 2; l <= n; ++l) // subproblem length for (int i = 0; i <= n - l; ++i) { // start int j = i + l - 1; for (int m = i; m < j ; m += K - 1) // split point dp[i][j] = min(dp[i][j], dp[i][m] + dp[m + 1][j]); // If the current length can be merged into 1 pile // The cost is independent of the merge orders. if ((l - 1) % (K - 1) == 0) dp[i][j] += sums[j + 1] - sums[i]; } return dp[0][n - 1]; } }; |
C++/Top-Down
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
// Author: Huahua, 4 ms class Solution { public: int mergeStones(vector<int>& stones, int K) { const int n = stones.size(); if ((n - 1) % (K - 1)) return -1; const int kInf = 1e9; vector<vector<int>> cache(n, vector<int>(n, kInf)); vector<int> sums(n + 1); for (int i = 0; i < stones.size(); ++i) sums[i + 1] = sums[i] + stones[i]; std::function<int(int, int)> dp; dp = [&stones, &sums, &cache, &dp, K, kInf](int i, int j) { const int l = j - i + 1; if (l < K) return 0; if (cache[i][j] != kInf) return cache[i][j]; int ans = kInf; for (int m = i; m < j ; m += K - 1) // split point ans = min(ans, dp(i, m) + dp(m + 1, j)); if ((l - 1) % (K - 1) == 0) ans += sums[j + 1] - sums[i]; return cache[i][j] = ans; }; return dp(0, n - 1); } }; |