[HAOI2016]字符合并 牛客(区间dp)

[HAOI2016]字符合并 牛客
链接:https://ac.nowcoder.com/acm/problem/19997
来源:牛客网
题意:
有一个长度为 n 的 01 串,你可以每次将相邻的 k 个字符合并,得到一个新的字符并获得一定分数。得到的新字符和分数由这 k 个字符确定。你需要求出你能获得的最大分数。

设dp[i][j][s] ,表示从i到j字符串能到状态s的最大得分。
k个字符可以替换为1个字符,也就是字符串的长度减去4.
情况1:对于i~j的子串,当的长度能缩减为1个字符 即:(j-i)%(k-1)=0时,
dp[i][j][a[l]]=max(dp[i][j][a[l]],dp[i][m][l>>1]+dp[m+1][j][l&1]+b[l]);
每次取m(mid),也就是说前部分字符串dp[i][j][l>>1]缩减为状态a[l]的前k-1 位,dp[i][j][l&1] 替换为最后一位,然后这k位缩减位1位,即a[l]的值。l>>1 取前k-1位 l&1 取最后一位。
a[l]是二进制第i个长度为k的字符串被替换后的字符。(0或1)
b[i]是对应的得分。

情况2:对于i~j的子串,长度不能缩减为1个字符,则令t=(j-i)%(k-1)+1,t是缩减后的字符串长度。
dp[i][j][l]=max(dp[i][j][l],dp[i][m][l>>1]+dp[m+1][j][l&1];
最后取dp[1][n][i] 的最大值就是结果。

#pragma GCC optimize("Ofast")
#pragma GCC target("avx,avx2,fma")
#pragma GCC optimization ("unroll-loops")
#include<queue>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<math.h>
#include<stdlib.h>
#include<stack>
#include<stdio.h>
#include<vector>
#include<set>
#include<bitset>
#include<map>
#include<string.h>
#include<complex>
#define ll long long
#define ull unsigned long long
#define db double
#define scan(n) scanf("%d",&n)
using namespace std;
int read(){
	int s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9') {
		if(ch=='-')w=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
	return s*w;
}
const int mx=1e6+10,inf=1e9+10,mod=1000000007;
int n,k;
int c[500],a[1000],b[1000];
ll dp[350][350][500];
string s; 
int main(){
#ifdef LOCAL_flyTY
freopen("in.txt","r",stdin);
#endif
	cin>>n>>k;
	cin>>s;
	int len=1<<k;
	for(int i=0;i<len;i++){
		scan(a[i]);
		scan(b[i]);
	}
	for(int i=1;i<=n;i++){
		for(int j=i;j<=n;j++){
			for(int k=0;k<=len;k++){
				dp[i][j][k]=-1e9;
			}
		}
	}
	for(int i=1;i<=n;i++){
		c[i]=s[i-1]-'0';
		dp[i][i][c[i]]=0;
	}
	for(int x=2;x<=n;x++){
		for(int i=1,j=x;j<=n;i++,j++){
			int t=(x-1)%(k-1)+1;
			if(t==1){
				for(int l=0;l<len;l++){
					for(int m=j-1;m>=i;m-=(k-1)){
						dp[i][j][a[l]]=max(dp[i][j][a[l]],dp[i][m][l>>1]+dp[m+1][j][l&1]+b[l]*1ll);
					}
				}
			}else{
				t=1<<t;
				for(int l=0;l<t;l++){
					for(int m=j-1;m>=i;m-=(k-1)){
						dp[i][j][l]=max(dp[i][j][l],dp[i][m][l>>1]+dp[m+1][j][l&1]);
					}
				}
			}
		}
	}
	ll ans=0;
	for(int i=0;i<len;i++){
		ans=max(ans,dp[1][n][i]);
	}
	cout<<ans<<endl;
	return 0;
} 
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值