SCUT125 华为杯 D.笔芯回文 —— DP
题目链接: https://scut.online/p/125
题目描述
bxbx有一个长度一个字符串SS,bxbx可以对其进行若干次操作。
每次操作可以删掉一个长度为k(1 \leq k \leq n)k(1≤k≤n)的连续回文子串,bxbx获得a_kak的愉悦值。
一个字符串是回文串当且仅当正读和反读都是一样的。例如"a", "aa", "abcba""a","aa","abcba"是回文串,"ab", "abc","aabab""ab","abc","aabab"不是回文串。
字符串删除之后相邻的字符不会合并在一起。
现在,bxbx想知道他最多能获得多少愉悦值。
输入格式
输入第一行一个整数TT,表示数据组数。
对于每组数据,第一行一个整数nn。
第二行nn个整数,第ii个表示a_iai。
第三行为字符串SS。
1 \leq T \leq 201≤T≤20
1 \leq n \leq |S| \leq 50001≤n≤∣S∣≤5000
0 \leq a_i \leq 10000000000≤ai≤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]);
代码如下:
1 #include <bits/stdc++.h> 2 using namespace std; 3 #define ms(a, b) memset((a), (b), sizeof(a)) 4 typedef long long LL; 5 const int INF = 2e9; 6 const LL LNF = 9e18; 7 const int mod = 1e9+7; 8 const int maxn = 5000+10; 9 10 char s[maxn]; 11 LL a[maxn], dp[maxn]; 12 bool ok[maxn][maxn]; 13 14 int n, len; 15 16 void init() 17 { 18 ms(ok,0); 19 ms(dp,0); 20 21 for(int i = 1; i<=len; i++) 22 { 23 int l = i, r = i; 24 while(l>=1 && r<=len && (r-l+1)<=n && s[l]==s[r]) 25 ok[l--][r++] = 1; 26 } 27 28 for(int i = 1; i<=len; i++) 29 { 30 int l = i, r = i+1; 31 while(l>=1 && r<=len && (r-l+1)<=n && s[l]==s[r]) 32 ok[l--][r++] = 1; 33 } 34 } 35 36 void solve() 37 { 38 for(int i = 1; i<=len; i++) 39 for(int j = 1; j<=i; j++) 40 { 41 if(ok[j][i]) 42 dp[i] = max(dp[i],dp[j-1]+a[i-j+1]); 43 } 44 45 printf("%lld\n",dp[len]); 46 } 47 48 int main() 49 { 50 int T; 51 scanf("%d",&T); 52 while(T--) 53 { 54 scanf("%d",&n); 55 for(int i = 1; i<=n; i++) 56 scanf("%lld",&a[i]); 57 58 scanf("%s", s+1); 59 len = strlen(s+1); 60 61 init(); 62 solve(); 63 } 64 return 0; 65 }