CF150D Mission Impassable
Div. 1 D. Mission Impassable
简要题意
给定一个字符串,删去一个长度为 \(k\) 的回文串的贡献为 \(a_k\),问这个字符串可以获得的最大贡献。
思路
由于答案的形态一定是消去若干段区间,因此可以想到区间 dp。而又由于题目说明了代价和回文串的长度强相关,于是考虑把长度这一维塞到状态里。
设 \(dp_{i, j, k}\) 表示把 \([i, j]\) 消到只剩长度为 \(k\) 的回文串可以获得的最大贡献。对于状态 \((i, j, k)\),其可以继续分治为 \((i, l - 1, 0), (l, r, k), (r + 1, j, 0)\)。而这种分治形式又等价于枚举一个分界点 \(p\),使得 \(l \le p, r > p\),所以该分治可以继续简化成 \((i, p, 0), (p + 1, j, k)\) 或 \((i, p, k), (i, p + 1, 0)\)。至于对 \(k\) 的转移,我们只需要处理其在端点的情况即可(因为其它转移都被第一种包含了),即 \((i + 1, j - 1, k - 2) \to (i, j, k)\)。最后是对某个区间选或不选的转移,可以额外子序列 dp。
代码:
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 155;
int n, a[N], g[N], f[N][N], dp[N][N][N];
string s;
signed main() {
ios_base :: sync_with_stdio(NULL);
cin.tie(nullptr);
cout.tie(nullptr);
memset(f, ~ 0x3f, sizeof f);
memset(dp, ~ 0x3f, sizeof dp);
cin >> n;
for(int i = 1 ; i <= n ; ++ i)
cin >> a[i];
cin >> s;
s = " " + s;
for(int i = 1 ; i <= n ; ++ i) {
dp[i][i][1] = 0;
if(a[1] >= 0) f[i][i] = a[1];
else f[i][i] = -2e18;
dp[i][i][0] = f[i][i];
}
for(int i = 1 ; i < n ; ++ i) {
if(a[1] >= 0) dp[i][i + 1][1] = a[1], f[i][i + 1] = a[1] * 2;
else dp[i][i + 1][1] = f[i][i + 1] = -2e18;
if(s[i] == s[i + 1]) {
dp[i][i + 1][2] = 0;
if(a[2] >= 0) f[i][i + 1] = max(f[i][i + 1], a[2]);
}
dp[i][i + 1][0] = f[i][i + 1];
}
for(int len = 3 ; len <= n ; ++ len) {
for(int i = 1 ; i <= n - len + 1 ; ++ i) {
int j = i + len - 1;
f[i][j] = -2e18;
for(int k = 1 ; k <= n ; ++ k) {
if(s[i] == s[j]) dp[i][j][k] = dp[i + 1][j - 1][k - 2];
else dp[i][j][k] = -2e18;
for(int l = i ; l < j ; ++ l)
dp[i][j][k] = max(dp[i][j][k], max(dp[i][l][k] + f[l + 1][j], f[i][l] + dp[l + 1][j][k]));
if(a[k] >= 0) f[i][j] = max(f[i][j], dp[i][j][k] + a[k]);
}
dp[i][j][0] = f[i][j];
}
}
for(int i = 1 ; i <= n ; ++ i) {
g[i] = g[i - 1];
for(int j = 1 ; j <= i ; ++ j)
g[i] = max(g[i], g[j - 1] + f[j][i]);
}
cout << g[n];
return 0;
}

浙公网安备 33010602011771号