【LeetCode】1000. Minimum Cost to Merge Stones(DP)
题目
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.
Notes:
(1)1 <= stones.length <= 30
(2)2 <= K <= 30
(3)1 <= stones[i] <= 100
题意
这题的大概意思是,一共有 n 堆石头,其中第 i 堆有stones[i] 个石头。
规定每次只能将连续的 K 堆石头合并成 1 堆,其花费为这 K 堆石头的石头总数。
要求找出将所有石头合并成 1 堆的最小花费,如果不可能合并成 1 堆,则返回 -1.
解题思路
这题应该采用dp来求解,关键在于找出dp递推公式。
假设 dp[i][j][k] 表示将第 i 到 第 j 堆石头合并成 1 堆石头的最小花费,初始的时候:
dp[i][i][0] = 0;
dp[i][i][m] = infinity; // 1 <= m <= K.
则递推公式为:
// stonesNumber[i][j]表示第i到第j堆石头的石头总数
// i <= mid < j
dp[i][j][k] = min(dp[i][mid][1] + dp[mid+1][j][k-1] + stonesNumber[i][j])
代码
#define INF 0xFFFF
class Solution {
public:
int ans[50][50][50];
int prex[50];
int mm[50][50][50];
int dp(int l, int r, int m, int k) {
int i, j, a, b, c;
if((r - l + 1 - m) % (k - 1)) {
return ans[l][r][m];
}
// 剪枝,否则会超时
if(mm[l][r][m]) {
return ans[l][r][m];
}
if(l == r) {
if(m == 1) {
ans[l][r][m] = 0;
return ans[l][r][m];
}
else {
// 返回的是无穷大,因为1堆石头不可能分成多堆
return ans[l][r][m];
}
}
if(m == 1) {
// 分成k堆
ans[l][r][m] = dp(l, r, k, k) + prex[r+1] - prex[l];
mm[l][r][m] = 1;
return ans[l][r][m];
}
for(i=l; i<r; i++) {
//左数第一堆的左边界是l,右边界可能是l到r-1
ans[l][r][m] = min(ans[l][r][m], dp(l, i, 1, k) + dp(i+1, r, m-1, k));
mm[l][r][m] = 1;
}
return ans[l][r][m];
}
int mergeStones(vector<int>& stones, int K) {
int i, j, a, len;
len = stones.size();
for(i=0; i<len; i++) {
for(j=0; j<len; j++) {
for(a=1; a<=K; a++) {
ans[i][j][a] = INF;
mm[i][j][a] = 0;
}
}
}
prex[0] = 0;
prex[1] = stones[0];
for(i = 2; i<=len; i++) {
prex[i] = prex[i-1] + stones[i-1];
}
int ret = dp(0, len-1, 1, K);
if(ret < INF) {
return ret;
}
return -1;
}
};