解题思路
我们把合并 1 至 n 1至n 1至n堆石子拆解成一个个子问题:
因为合并相邻两堆石子的值我们是可以确定的,已知任意两堆相邻的石子合并的代价之后,对于三堆石子,我们可以考虑两种方法
- 先合并 1 至 2 1至2 1至2堆,再把前 2 2 2堆和第 3 3 3堆石子合并。
- 先合并 2 至 3 2至3 2至3堆,再把后 2 2 2堆和第 1 1 1堆石子合并。
对于题目样例,4堆石子,代价分别为4,5,9,4。
①先考虑合并两堆石子的情况
| 合并区间 | 1,2 | 2,3 | 3,4 |
|---|---|---|---|
| 代价 | 9 | 14 | 13 |
②考虑合并三堆石子的情况:
1)前三堆石子合并,如果先合并 1 至 2 1至2 1至2堆,再把前 2 2 2堆和第 3 3 3堆石子合并,代价为 9 + 4 + 5 + 9 = 27 9+4+5+9=27 9+4+5+9=27;如果先合并 2 至 3 2至3 2至3堆,再把后 2 2 2堆和第 1 1 1堆石子合并,代价为 9 + 5 + 4 + 5 + 9 = 32 9+5+4+5+9=32 9+5+4+5+9=32。取较小值,27。
2)后三堆石子合并,如果先合并 2 至 3 2至3 2至3堆,再把 2 至 3 2至3 2至3堆和第 4 4 4堆石子合并,代价为 5 + 9 + 5 + 9 + 4 = 32 5+9+5+9+4=32 5+9+5+9+4=32;如果先合并 3 至 4 3至4 3至4堆,再把 3 至 4 3至4 3至4堆和第 2 2 2堆石子合并,代价为 9 + 4 + 5 + 9 + 4 = 31 9+4+5+9+4=31 9+4+5+9+4=31。取较小值,31。
③考虑合并四堆石子的情况:
1)前三堆石子和第四堆石子合并,根据②,前三堆石子合并的最小代价为27,再和第四堆石子合并,代价为 27 + 4 + 5 + 9 + 4 = 49 27+4+5+9+4=49 27+4+5+9+4=49。
2)前两堆石子和后两堆石子合并,根据①,前两堆石子合并的最小代价为9,后两堆石子合并的最小代价为13,前两堆石子和后两堆石子合并的代价为 9 + 13 + 4 + 5 + 9 + 4 = 44 9+13+4+5+9+4=44 9+13+4+5+9+4=44。
3)第一堆石子和后三堆石子合并,根据②,后三堆石子合并的最小代价为31,一堆石子和后三堆石子合并的最小代价为 31 + 4 + 5 + 9 + 4 = 53 31+4+5+9+4=53 31+4+5+9+4=53。
实现算法
前面的样例模拟过程可以总结以下过程:
- 我们设 d p l r dplr dplr为合并区间 l , r l,r l,r内的石子的最小代价。 d p dp dp数组初始化为0。
- 我们通过前面的分析知道,合并区间 l , r l,r l,r内的石子的最小代价, d p l r = m i n ( d l k + d p k + 1 r ) + 区间 l , r 内所有石子的重量 ( l < = k < r ) dplr=min(dlk+dpk+1r)+区间l,r内所有石子的重量(l<=k<r) dplr=min(dlk+dpk+1r)+区间l,r内所有石子的重量(l<=k<r)。区间内石子的重量,我们实际上可以做一个计算前缀和的预处理,前缀和结果存储在数组 s s s中,这样查询区间 l , r l,r l,r内所有石子的重量就更高效了。
- 做完准备工作后,执行以下过程:
1)我们从2开始枚举所有可能的长度 l n ln ln;
2)在枚举长度的循环内,枚举区间的左右端点 l l l和 r r r, l l l从1开始枚举, r r r从 l + l n − 1 l+ln-1 l+ln−1开始枚举,结束条件是 r < = n r<=n r<=n;
3)在枚举区间左右端点的循环内,我们还有枚举断点 k ( l < = k < r ) k(l<=k<r) k(l<=k<r),枚举 k k k的循环中执行状态转移方程: d p l r = m i n ( d p l r , d p l k + d p k + 1 r + s r − s l − 1 ) dplr=min(dplr,dplk+dpk+1r+sr-sl-1) dplr=min(dplr,dplk+dpk+1r+sr−sl−1)。
4)最后输出答案: d p 1 n dp1n dp1n
AC代码
cpp
#include <bits/stdc++.h>
using namespace std;
#define int long long
int n, m[1005], s[1005], dp[1005][1005];
signed main() {
cin >> n;
memset(dp, 0x3f, sizeof(dp));
for (int i = 1; i <= n; i++) {
cin >> m[i];
s[i] = s[i - 1] + m[i];
dp[i][i] = 0;
}
for (int ln = 2; ln <= n; ln++) {
for (int l = 1, r = l + ln - 1; r <= n; l++, r++) {
for (int k = l; k < r; k++) {
dp[l][r] = min(dp[l][r], dp[l][k] + dp[k + 1][r] + s[r] - s[l - 1]);
}
}
}
cout << dp[1][n];
return 0;
}