区间dp
合并:即将两个或多个部分进行整合,当然也可以反过来;
特征:能将问题分解为能两两合并的形式;
求解:对整个问题设最优值,枚举合并点,将问题分解为左右两个部分,最后合并两个部分的最优值得到原问题的最优值。
例题
P1880 [NOI1995] 石子合并 - 洛谷
可以先将圆环扩成一条链。
定义 \(dp_{i,j,k}\) 为第 \(i\) 堆石子到第 \(j\) 堆石子的最小/大代价,\(sum_i\) 表示前 \(i\) 堆石子的质量和。
从小到大枚举长度进行区间dp即可。
代码
| C++ |
|---|
| #include <bits/stdc++.h>
using namespace std;
using i64 = long long;
int __OneWan_2024 = [](){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
return 0;
}();
int dp[305][305][2], a[305], sum[305];
int main() {
int n;
cin >> n;
for (int i = 1 ; i <= n ; i++) {
cin >> a[i];
a[i + n] = a[i];
}
for (int i = 1 ; i <= 2 * n ; i++) {
for (int j = i ; j <= 2 * n ; j++) {
dp[i][j][0] = 1e9;
dp[i][j][1] = -1e9;
}
dp[i][i][0] = 0;
dp[i][i][1] = 0;
}
for (int i = 1 ; i <= 2 * n ; i++) {
sum[i] = sum[i - 1] + a[i];
}
for (int len = 2 ; len <= 2 * n ; len++) {
for (int L = 1 ; L + len - 1 <= 2 * n ; L++) {
int R = L + len - 1;
int s = sum[R] - sum[L - 1];
for (int k = L ; k < R ; k++) {
dp[L][R][0] = min(dp[L][R][0], dp[L][k][0] + dp[k + 1][R][0] + s);
dp[L][R][1] = max(dp[L][R][1], dp[L][k][1] + dp[k + 1][R][1] + s);
}
}
}
int mi = 1e9, mx = -1e9;
for (int i = 1 ; i <= n ; i++) {
mi = min(mi, dp[i][i + n - 1][0]);
mx = max(mx, dp[i][i + n - 1][1]);
}
cout << mi << "\n" << mx;
return 0;
}
|
习题
Problem - D - Codeforces
定义 \(dp_{i,j}\) 为第 \(i\) 个数到第 \(j\) 个数的最大得分,\(prod_i\) 表示前 \(i\) 个数乘积。
从小到大枚举长度进行区间dp即可。
代码
| C++ |
|---|
| #include <bits/stdc++.h>
using namespace std;
using i64 = long long;
int __OneWan_2024 = [](){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
return 0;
}();
i64 a[305], prod[305];
i64 dp[305][305];
const i64 P = 1000003;
i64 inv(i64 a, i64 b = P - 2, i64 res = 1) {
while (b) {
if (b & 1) {
res = res * a % P;
}
a = a * a % P;
b >>= 1;
}
return res;
}
int main() {
int n;
cin >> n;
for (int i = 1 ; i <= n ; i++) {
cin >> a[i];
}
prod[0] = 1;
for (int i = 1 ; i <= n ; i++) {
prod[i] = prod[i - 1] * a[i] % P;
}
for (int len = 2 ; len <= n ; len++) {
for (int L = 1 ; L + len - 1 <= n ; L++) {
int R = L + len - 1;
for (int k = L ; k < R ; k++) {
i64 s = prod[R] * inv(prod[k] % P) % P - prod[k] * inv(prod[L - 1]) % P;
s = s * s;
dp[L][R] = max(dp[L][R], dp[L][k] + dp[k + 1][R] + s);
}
}
}
cout << dp[1][n];
return 0;
}
|