VIrtuoso

两把多兰剑加个布甲鞋

导航

Educational Codeforces Round 59 (Rated for Div. 2) E 区间dp + 状态定义 + dp预处理(分步dp)

https://codeforces.com/contest/1107/problem/E

题意

给出01字符串s(n<=100),相邻且相同的字符可以同时消去,一次性消去i个字符的分数是\(a[i]\),问消去s最多能得到多少分数

题解

  • 实质是安排消去次序使得分数最大,第一步采取的行动是递归边界
  • 因为只有01串,所以s被分成了一段一段,考虑段处理
  • 预处理出一次性消去i个字符的最大分数\(f[i]\)
  • 定义\(dp[l][r][cnt]\)为消去第l到第r段加上cnt个字符和第l段相同得到的最大分数
  • 每个区间只考虑第l段消去的情况(立刻消去or和后面的一起消去)
ans=dfs(l+1,r,0)+f[cnt+b[l]];  //立刻消去第l段
for(int i=l+2;i<=r;i+=2){
    ans=max(ans,dfs(l+1,i-1,0)+dfs(i,r,b[l]+cnt)); //枚举第二段的分割点,dfs(第二段)+dfs(第l段+第三段)
}

代码

#include<bits/stdc++.h>
#define ll long long 
#define MAXN 105
using namespace std;
ll f[MAXN],dp[MAXN][MAXN][MAXN],a[MAXN];
vector<int>b;
string s;
int n,cnt=0;
ll dfs(int l,int r,int cnt){
	if(l>r)return f[cnt];
	ll &ans=dp[l][r][cnt];
	if(ans!=-1)return ans;
	ans=dfs(l+1,r,0)+f[cnt+b[l]];
	for(int i=l+2;i<=r;i+=2)
		ans=max(ans,dfs(l+1,i-1,0)+dfs(i,r,b[l]+cnt));
	return ans;
}
int main(){
	cin>>n>>s;
	for(int i=1;i<=n;i++)cin>>a[i];
	f[1]=a[1];
	for(int i=2;i<=n;i++){
		f[i]=a[i];
		for(int j=1;j<i;j++)
			f[i]=max(f[i],f[j]+f[i-j]);
	}
	memset(dp,-1,sizeof(dp));
	for(int i=0;i<n;i++){
		if(i==0||s[i]==s[i-1])cnt++;
		else{
			b.push_back(cnt);
			cnt=1;
		}
	}
	b.push_back(cnt);
	cout<<dfs(0,b.size()-1,0);
}

posted on 2019-05-04 16:02  VIrtuoso  阅读(181)  评论(0编辑  收藏  举报