SCUT125 华为杯 D.笔芯回文 —— DP

题目链接: https://scut.online/p/125

题目描述

bxbx有一个长度一个字符串SS,bxbx可以对其进行若干次操作。

每次操作可以删掉一个长度为k(1 \leq k \leq n)k(1≤k≤n)的连续回文子串,bxbx获得a_ka​k​​的愉悦值。

一个字符串是回文串当且仅当正读和反读都是一样的。例如"a", "aa", "abcba""a","aa","abcba"是回文串,"ab", "abc","aabab""ab","abc","aabab"不是回文串。

字符串删除之后相邻的字符不会合并在一起。

现在,bxbx想知道他最多能获得多少愉悦值。

输入格式

输入第一行一个整数TT,表示数据组数。

对于每组数据,第一行一个整数nn。

第二行nn个整数,第ii个表示a_ia​i​​。

第三行为字符串SS。

1 \leq T \leq 201≤T≤20

1 \leq n \leq |S| \leq 50001≤n≤∣S∣≤5000

0 \leq a_i \leq 10000000000≤a​i​​≤1000000000

SS只包括小写字母。

输出格式

对每组数据,输出bxbx所能获得的最大愉悦值。

样例数据

输入

2
3
1 2 3
aba
3
3 2 1
aba

输出

3
9

题解:

1.ok[l][r]代表区间l~r的子串是否为回文串,O(n^2)预处理。

2. dp[i]代表删除前i个字符的最大价值, 状态转移方程为:if(ok[j][i])  dp[i] = max(dp[i],dp[j-1]+a[i-j+1]);

代码如下:

 #include <bits/stdc++.h>
using namespace std;
#define ms(a, b) memset((a), (b), sizeof(a))
typedef long long LL;
const int INF = 2e9;
const LL LNF = 9e18;
const int mod = 1e9+;
const int maxn = +; char s[maxn];
LL a[maxn], dp[maxn];
bool ok[maxn][maxn]; int n, len; void init()
{
ms(ok,);
ms(dp,); for(int i = ; i<=len; i++)
{
int l = i, r = i;
while(l>= && r<=len && (r-l+)<=n && s[l]==s[r])
ok[l--][r++] = ;
} for(int i = ; i<=len; i++)
{
int l = i, r = i+;
while(l>= && r<=len && (r-l+)<=n && s[l]==s[r])
ok[l--][r++] = ;
}
} void solve()
{
for(int i = ; i<=len; i++)
for(int j = ; j<=i; j++)
{
if(ok[j][i])
dp[i] = max(dp[i],dp[j-]+a[i-j+]);
} printf("%lld\n",dp[len]);
} int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(int i = ; i<=n; i++)
scanf("%lld",&a[i]); scanf("%s", s+);
len = strlen(s+); init();
solve();
}
return ;
}
上一篇:C puzzles详解【26-30题】


下一篇:C puzzles详解【31-33题】