标签:表示 前缀 scan n+2 线性 lin mes 元素 出现
区间类动态规划是线性动态规划的扩展,它在分阶段地划分问题时,与阶段中元素出现的顺序和由前一阶段的哪些元素合并而来由很大的关系。令状态 \(f(i,j)\) 表示将下标位置 \(i\) 到 \(j\) 的所有元素合并能获得的价值的最大值,那么 \(f(i,j)=max\{ f(i,k)+f(k+1,j)+cost \}\) , \(cost\) 为将这两组元素合并起来的代价。 ——转自 OI Wiki
简单来说,区间动归的一大特点就是,将多个部分整合(或者反过来)。求解的时候,可以将一段区间分成两部分来处理。由于先处理的是区间短的情况,所以就可以递推出完整区间。
考虑一条链的情况,用 \(dp[i][j]\) 来表示区间 \([i,j]\) 下的最优解(比如,最大值):
for (int len = 1; len <= n - 1; len++)
for (int l = 1; l + len <= n; l++)
for (int k = l + 1; k <= r; k++)
dp[l][r] = max(dp[l][r], dp[l][k - 1] + dp[k][r] + cost(l, r);
而对于环形的区间DP题,区间不一定是 \([i,j](i\leqslant j)\) ,最后结果的最优解也不一定是 \([1,n]\) ,而可能是 \([3,2]\) ,这对我们的循环造成了麻烦。然鹅这样的区间都是由 \([j,n]\) 和 \([1,i]\) 组成的,如果我们把环拆成链,再在 \(n\) 后接上 \(1\sim n\) 同样的一条链,就能解决问题。
原数据 | 1 | 2 | 3 | …… | n | 1 | 2 | 3 | …… | n |
---|---|---|---|---|---|---|---|---|---|---|
存入数组 | a[1] | a[2] | a[3] | …… | a[n] | a[n+1] | a[n+2] | a[n+3] | …… | a[2n] |
区间 \([j,i](i\leqslant j)\) 等价于 \([j,n+i]\) ,区间正常了,随后的递推和链状DP是一样的。不过最后输出结果的时候还要注意一点,最优解不一定是 \([1,n]\) 了,也有可能是 \([2,n+1]\) 等等,需要用循环来跑一遍。
核心代码
for (int len = 1; len <= n - 1; len++)
for (int l = 1; l + len <= 2 * n; l++)
{
int r = l + len;
for (int k = l + 1; k <= r; k++)
dp[l][r] = max(dp[l][r], dp[l][k - 1] + dp[k][r] + cost(l, r));
}
int ans = 0;
for (int i = 1; i <= n; i++)
ans = max(ans, dp[i][i + n - 1]);
看来对一条链的区间DP过于简单,连道题目都没找到(逃) 打脸了,找一道看吧:P3146 【USACO16OPEN】248 G
#include <bits/stdc++.h>
using namespace std;
int n, ans = 0, dp[250][250];
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++)
{
scanf("%d", &dp[i][i]);
ans = max(ans, dp[i][i]);
}
for (int len = 1; len <= n - 1; len++)
for (int l = 1; l + len <= n; l++)
{
int r = l + len;
for (int k = l + 1; k <= r; k++)
if (dp[l][k - 1] == dp[k][r] && dp[l][k - 1])
dp[l][r] = max(dp[l][r], dp[l][k - 1] + 1);
ans = max(ans, dp[l][r]);
}
printf("%d\n", ans);
return 0;
}
#include <bits/stdc++.h>
using namespace std;
int n, a[205], sum[205] = {0}, dp_min[205][205] = {0}, dp_max[205][205] = {0};
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++)
{
scanf("%d", a + i);
a[n + i] = a[i];
}
for (int i = 1; i <= 2 * n; i++)
sum[i] = sum[i - 1] + a[i]; //前缀和为 cost 函数做好铺垫
for (int len = 1; len <= n - 1; len++)
for (int l = 1; l + len <= 2 * n; l++)
{
int r = l + len;
dp_min[l][r] = __INT_MAX__;
for (int k = l + 1; k <= r; k++)
{
dp_min[l][r] = min(dp_min[l][r], dp_min[l][k - 1] + dp_min[k][r] + sum[r] - sum[l - 1]);
dp_max[l][r] = max(dp_max[l][r], dp_max[l][k - 1] + dp_max[k][r] + sum[r] - sum[l - 1]);
}
}
int ans_min = __INT_MAX__, ans_max = 0;
for (int i = 1; i <= n; i++)
{
ans_min = min(ans_min, dp_min[i][i + n - 1]);
ans_max = max(ans_max, dp_max[i][i + n - 1]);
}
printf("%d\n%d\n", ans_min, ans_max);
return 0;
}
标签:表示 前缀 scan n+2 线性 lin mes 元素 出现
原文地址:https://www.cnblogs.com/guapiii/p/13233274.html