392E - Deleting Substrings
分类:
dp
题意: 有一长度为 n ( 1 ≤ n ≤ 400 ) n\ (1\leq n\leq 400) n (1≤n≤400) 的序列 W ( 1 ≤ w i ≤ 1 0 9 ) W\ (1\leq w_i\leq 10^9) W (1≤wi≤109) 和 V ( 0 ≤ ∣ v i ∣ ≤ 2000 ) V\ (0\leq |v_i|\leq 2000) V (0≤∣vi∣≤2000) ,对于它的一段子序列 w l w_l wl 、 w l + 1 w_{l+1} wl+1 、…、 w r w_r wr, 如果满足 ∣ w i − w i + 1 ∣ = 1 |w_i-w_i+1|=1 ∣wi−wi+1∣=1 且 2 ⋅ w i − w i + 1 − w i − 1 ≥ 0 2·w_i-w_{i+1}-w_{i-1}≥ 0 2⋅wi−wi+1−wi−1≥0 ,那么你可以将这一段删除,并获得 v r − l + 1 v_{r-l+1} vr−l+1 的价值,任意时刻都可以停止操作(没删完也行),现在问你获得的最大价值和是多少?
题解: 显然能删的就是连续的一段上升、下降或者锯齿区间,不妨考虑动态规划, f ( i , j ) f(i,j) f(i,j) 表示把区间 [ i , j ] [i,j] [i,j] 删除的最大价值和, g ( i , j ) g(i,j) g(i,j) 表示把区间删成连续上升一段的最大价值和, h ( i , j ) h(i,j) h(i,j) 表示把区间删成连续下降一段的最大价值和,那么转移方程可以是 f ( i , j ) = m a x { f ( i , k ) + f ( k + 1 , j ) , g ( i , k ) + h ( k , j ) + v 2 ⋅ w k − w i − w j + 1 } f(i,j)=max\{f(i,k)+f(k+1,j),g(i,k)+h(k,j)+v_{2·w_k-w_i-w_j+1}\} f(i,j)=max{f(i,k)+f(k+1,j),g(i,k)+h(k,j)+v2⋅wk−wi−wj+1} ,后者也很容易理解,我考虑了锯齿一段(这里只考虑上凸的锯齿,下凹的是一样的状态),显然当只有 g ( i , k ) g(i,k) g(i,k) 存在时候,左边区间的大小就是 w k − w i + 1 w_k-w_i+1 wk−wi+1 ,右边区间的大小就是 w k − w j + 1 w_k-w_j+1 wk−wj+1 ,当然区间有一个重叠的地方就是 k k k 位置,因此长度就是 2 ⋅ w k − w i − w j + 1 2·w_k-w_i-w_j+1 2⋅wk−wi−wj+1 !最后答案就是 f f f 的最大子序列和!接下来就是枚举的细节问题了。还是挺常见的一种区间DP处理方法。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int f[404][404], g[404][404], h[404][404], w[404], v[404], a[404];
int main() {
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &v[i]);
for (int i = 1; i <= n; i++) scanf("%d", &w[i]);
for (int i = 1; i <= n; i++) {
f[i][i] = v[1];
g[i][i] = h[i][i] = 0;
}
for (int l = 1; l <= n; l++) {
for (int i = 1; i + l - 1 <= n; i++) {
int j = i + l - 1;
if (i == j) continue;
f[i][j] = g[i][j] = h[i][j] = -1e8;
for (int k = i + 1; k <= j; k++) {
if (w[i] + 1 == w[k]) g[i][j] = max(g[i][j], f[i + 1][k - 1] + g[k][j]);
if (w[i] - 1 == w[k]) h[i][j] = max(h[i][j], f[i + 1][k - 1] + h[k][j]);
f[i][j] = max(f[i][j], f[i][k - 1] + f[k][j]);
}
for (int k = i; k <= j; k++) {
ll len = 2LL * w[k] - w[i] - w[j] + 1;
if (0 <= len && len <= n) f[i][j] = max(f[i][j], g[i][k] + h[k][j] + v[len]);
}
}
}
a[0] = 0;
for (int j = 1; j <= n; j++) {
a[j] = a[j - 1];
for (int i = 1; i <= j; i++) a[j] = max(a[j], a[i - 1] + f[i][j]);
}
printf("%d\n", a[n]);
return 0;
}