区间动态规划
区间类动态规划的问题解法比较固定:以区间长度为阶段,枚举区间长度,再枚举区间左端点,之后再枚举区间断点进行转移。
AcWing 282. 石子合并
分析:若最初的第
另外,一定存在一个整数
对应动态规划中,这就意味着两个长度更小的区间上的信息转移到更长的区间上,划分点
边界条件:
目标解:
#include <bits/stdc++.h>
using namespace std;
const int N = 310;
int s[N];
int f[N][N];
int n;
int main() {
cin >> n;
for (int i = 1; i <= n; i++) cin >> s[i];
for (int i = 1; i <= n; i++) s[i] += s[i - 1];
for (int len = 2; len <= n; len++) {
for (int i = 1; i + len - 1 <= n; i++) {
int l = i, r = i + len - 1;
f[l][r] = 1e9;
for (int k = l; k < r; k++) {
f[l][r] = min(f[l][r], f[l][k] + f[k + 1][r] + s[r] - s[l - 1]);
}
}
}
cout << f[1][n] << endl;
return 0;
}AcWing 1068. 环形石子合并
与上例不同的是:这里的石子是围成了一个环
分析:由上题思考,对于环形相邻问题一般有两种解决办法:
- 枚举环分开的位置,将环还原成链。时间复杂度
- 将[[链延长两倍。]]时间复杂度为
涉及到圆环。我们可以用“破环成链”。一般使用第二种方案:链的长度是环的两倍,我们在
#include <bits/stdc++.h>
using namespace std;
const int N = 210 * 2;
int a[N], s[N];
int f[N][N], g[N][N]; // f 存储最大值,g存储最小值
int n;
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
a[n + i] = a[i];
}
for (int i = 1; i <= 2 * n; i++) s[i] = s[i - 1] + a[i];
memset(f, -0x3f, sizeof f);
memset(g, +0x3f, sizeof g);
for (int len = 1; len <= n; len++) {
for (int i = 1; i + len - 1 < 2 * n; i++) {
int l = i, r = i + len - 1;
if (len == 1) f[l][r] = g[l][r] = 0;
else {
for (int k = l; k + 1 <= r; k++) {
f[l][r] = max(f[l][r], f[l][k] + f[k + 1][r] + s[r] - s[l - 1]);
g[l][r] = min(g[l][r], g[l][k] + g[k + 1][r] + s[r] - s[l - 1]);
}
}
}
}
int maxv = -0x3f3f3f3f, minv = 0x3f3f3f3f;
for (int l = 1; l <= n; l++) {
maxv = max(maxv, f[l][l + n - 1]);
minv = min(minv, g[l][l + n - 1]);
}
cout << minv << endl << maxv << endl;
return 0;
}洛谷 P1063:能量项链
分析:这道题每一个珠子的首尾可以看成是一个矩阵的左右两端点,合并两个珠子
设
为什么这里的是
边界条件:
目标解:
#include <bits/stdc++.h>
using namespace std;
const int N = 210;
int a[N], f[N][N];
int n;
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
a[n + i] = a[i];
}
// 长度从 3 开始,长度为 2 时无法释放能量
for (int len = 3; len <= n + 1; len++) {
for (int i = 1; i + len - 1 <= 2 * n; i++) {
int l = i, r = i + len - 1;
// 分界点是从 l + 1 开始的
for (int k = l + 1; k < r; k++) {
f[l][r] = max(f[l][r], f[l][k] + f[k][r] + a[l] * a[k] * a[r]);
}
}
}
int res = 0;
for (int l = 1; l <= n; l++) {
res = max(res, f[l][l + n]);
}
cout << res << endl;
return 0;
}AcWing 1069. 凸多边形的划分
总结与习题
总结
区间 DP 的基本特征是能将问题分解成为两两合并的形式。解决方法是区间长度为阶段,枚举划分点,将问题分解成为左右两个部分,合并最后的两个部分的最优解得到原问题的最优解。
状态转移方程一般形式如下:
习题
请使用区间 DP 的思考方式完成以下两题:
