[Haoi2016]字符合并---区间+状压dp+分析

为了方便将原题题意中的k改成了m(为什么方便??)
毒瘤有趣理由:因为毒瘤,主要在于分析

Description

有一个长度为 n 的 01 串,你可以每次将相邻的 m 个字符合并,得到一个新的字符并获得一定分数。得到的新字符和分数由这 m 个字符确定。你需要求出你能获得的最大分数。

Input

第一行两个整数n,m。接下来一行长度为n的01串,表示初始串。接下来2^k行,每行一个字符ci和一个整数wi,ci表示长度为k的01串连成二进制后按从小到大顺序得到的第i种合并方案得到的新字符,wi表示对应的第i种方案对应获得的分数。

1<=n<=300,0<=ci<=1,wi>=1,m<=8

Solution

首先观察到m很小,n只有300。这种区间操作的题目,很容易想到区间dp,由于合并后会产生新的字符,造成一定的影响,所以考虑多加1维状态。于是就有了
f [ l ] [ r ] [ s ] : [ l , r ] 01 s s 01 f[l][r][s]:区间[l,r]合并生成01串s所能获得的最大分数 \\s为状压后的01序列
由于我们考虑的是区间最优,所以 s s 必定不会大于 2 m 2^m

大概写个转移,考虑s的最后一位是由哪一段区间得到的
f [ l ] [ r ] [ s ] = m a x ( f [ l ] [ k 1 ] [ s &gt; &gt; 1 ] + f [ k ] [ r ] [ s   a n d   1 ] ) f[l][r][s]=max(f[l][k-1][s&gt;&gt;1]+f[k][r][s\ and \ 1])
这个转移是不完整的,并且如果挨个枚举k超时的可能性很大

接下来考虑一个问题,多少个连续字符可以合并成1个字符

1个字符可以不操作,m个连续字符可以合并成1个字符.于是得到 x ( m 1 ) + 1 x\ast(m-1)+1 个连续字符,也能合并成一个字符。

所以当 ( r l + 1 1 ) 0 ( m o d   ( m 1 ) ) (r-l+1-1)\equiv0 (mod\ (m-1)) 时有这样一种转移,即这一段区间也可以合并成一个字符(在实际实现的时候,需要开临时数组来转移)
f [ l ] [ r ] [ 0 ] = m a x ( c [ s ] = 0 , f [ l ] [ r ] [ s ] + w [ s ] ) f [ l ] [ r ] [ 1 ] = m a x ( c [ s ] = 1 , f [ l ] [ r ] [ s ] + w [ s ] ) f[l][r][0]=max(\forall c[s]=0,f[l][r][s]+w[s])\\f[l][r][1]=max(\forall c[s]=1,f[l][r][s]+w[s])
又想到之前的转移,枚举 k k ,使区间 [ k , r ] [k,r] 合并成一个字符。 k k 从r开始,由于上述结论,k每次可以-(m-1)

Code

#include <bits/stdc++.h>
using namespace std;
int n,m,a[310],c[300],w[300];
long long f[310][310][300];
char S[310];
int main()
{
	scanf("%d%d",&n,&m);
	scanf("%s",S+1);
	for (int i=1;i<=n;i++)
	  a[i]=int(S[i]-48);
	for (int i=0;i<(1<<m);i++)
  	  scanf("%d%d",&c[i],&w[i]);
  	memset(f,-10,sizeof(f)); 
  	for (int i=1;i<=n;i++) f[i][i][a[i]]=0; 
  	for (int len=2;len<=n;len++) 
  		for (int l=1;l<=n-len+1;l++) {
		    int r=len+l-1;
		    for (int s=0;s<(1<<m);s++) 
		  	  for (int k=r;k>l;k-=(m-1))
		  	    f[l][r][s]=max(f[l][r][s],f[l][k-1][s>>1]+f[k][r][s&1]);
		    if ((len-1)%(m-1)==0) {
		    	long long g[2];
				g[0]=g[1]=-1e12; 
		  	    for (int s=0;s<(1<<m);s++)
		  	      g[c[s]]=max(g[c[s]],f[l][r][s]+w[s]);
		  		f[l][r][0]=g[0];f[l][r][1]=g[1];
		    }	
		  }
	long long ans=0;
	for (int i=0;i<(1<<m);i++)
	  ans=max(ans,f[1][n][i]);
	cout<<ans;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/yu25_21_5/article/details/100889239