分析
此题做法有好几种,其中一种是 dp,还有一种是记忆化搜索。
我们知道暴力 dfs 非常慢,但是加上记忆化优化后会原地起飞。
dfs(l,r)
表示合并区间
[
l
,
r
]
[l,r]
[l,r] 的最小价值,那么我们枚举
[
l
,
r
]
[l,r]
[l,r] 间的一个中转点
i
i
i,答案就是:
- 不合并;
- 合并,加上代价;
取最小值。
dfs(l,r)=min(dfs(l,i)+dfs(i+1,r)+a[r]-a[l-1]);
a a a 数组存的是前缀和,快速求合并 [ l , r ] [l,r] [l,r] 的代价。
代码
#include <bits/stdc++.h>
using namespace std;
int a[1005], f[1005][1005], n;
int dfs(int l, int r) {
if (l == r)return 0;//自己与自己合并,代价为0
if (f[l][r] != INT_MAX)return f[l][r];//记忆化,已经求解的直接返回
for (int i = l; i < r; ++i) {
f[l][r] = min(f[l][r], dfs(l, i) + dfs(i + 1, r) + a[r] - a[l - 1]);
}
return f[l][r];
}
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
f[i][j] = INT_MAX;
}
}
for (int i = 1; i <= n; i++)cin >> a[i], a[i] += a[i - 1];
cout << dfs(1, n);
return 0;
}
区间动归 dfs 模板
最后,安利一个区间合并的 dfs 模板。其实就是上面复制下来的~
int dfs(int l, int r) {
if (l == r)return 0;
if (f[l][r] != INT_MAX)return f[l][r];
for (int i = l; i < r; ++i) {
f[l][r] = min(f[l][r], dfs(l, i) + dfs(i + 1, r) + a[r] - a[l - 1]);
}
return f[l][r];
}