Codeforces 1107E(区间dp)

用solve(l, r, prefix)代表区间l开始r结束、带了prefix个前缀str[l](即l前面的串化简完压缩成prefix-1个str[l],加上str[l]共有prefix个)的最大值。

每层可以选择:

1.直接“提现”,把起始位和前面的“存款”直接提出来,再计算l+1~r区间的值;

2.继续“存款”,枚举和str[l]相同的位置,把中间先合并了然后删了,把prefix+1,即把枚举的这位和之前的存款粘在一起,然后计算i~r的值,更新ans。

 

乍一想会觉得每次只把两个粘在一起这行吗,实际上一个区间里可能会好几个粘在一起啊?但其实:假设i < j,算i的时候i~r的那部分的存款就为prefix+1了,即我们选取j的时候,i已经在里了,所以只是子区间看起来选两个而已,实际上从大的范围来看已经选了若干个。

 

然后这个方法居然不用提前预处理——真·a[i],可能是搞的过程中就背包了吧?只是预处理的会跑得快一些。

 

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <iostream>
 4 #include <algorithm>
 5 using namespace std;
 6 
 7 typedef long long ll;
 8 int n;
 9 ll a[105], dp[105][105][105];
10 char s[105];
11 
12 ll solve(int l, int r, int prefix) {
13     if (l > r)
14         return 0;
15     if (l == r)
16         return a[prefix];
17 
18     ll &ret = dp[l][r][prefix];
19     if (ret)
20         return ret;
21 
22     ret = a[prefix] + solve(l + 1, r, 1);
23     for (int i = l + 1; i <= r; i++) {
24         if (s[i] == s[l])
25             ret = max(ret, solve(l + 1, i - 1, 1) + solve(i, r, prefix + 1));
26     }
27 
28     return ret;
29 }
30 
31 int main() {
32     cin >> n >> (s + 1);
33 
34     for (int i = 1; i <= n; i++)
35         cin >> a[i];
36 
37     for (int i = 2; i <= n; i++) {
38         for (int j = i; j > 0; j--) {
39             a[i] = max(a[i], a[j] + a[i - j]);
40         }
41     }
42 
43     cout << solve(1, n, 1) << endl;
44     return 0;
45 }

 

posted @ 2019-03-29 16:01  AlphaWA  阅读(175)  评论(0编辑  收藏  举报