[Haoi2016]字符合并 题解
tijie
时间限制: 2 Sec 内存限制: 256 MB题目描述
有一个长度为 n 的 01 串,你可以每次将相邻的 k 个字符合并,得到一个新的字符并获得一定分数。得到的新字
符和分数由这 k 个字符确定。你需要求出你能获得的最大分数。
输入
第一行两个整数n,k。接下来一行长度为n的01串,表示初始串。接下来2k行,每行一个字符ci和一个整数wi,ci
表示长度为k的01串连成二进制后按从小到大顺序得到的第i种合并方案得到的新字符,wi表示对应的第i种方案对应
获得的分数。1<=n<=300,0<=ci<=1,wi>=1,k<=8
输出
输出一个整数表示答案
样例输入
3 2
101
1 10
1 10
0 20
1 30
样例输出
40
//第3行到第6行表示长度为2的4种01串合并方案。00->1,得10分,01->1得10分,10->0得20分,11->1得30分。
这道题当时是按照搜索或动归去做的,然而,想不出动归状态咋搞啊,于是乎,花了不到半个小时打了一个MLE的爆搜果断挂了,爆零。
其实正解真的是DP,只是状态蛮有意思的,这是一种区间动归的思想,因为他合并只是一段串合并,对左右两端的串无直接影响而n,k也不大,因此我们考虑一下状压,当时不是没想过这点,只是压什么,怎么压是个问题,我们自然是不可能去压全串的状态,但我们可以稍微算一下,区间动归所需的状态数组加上状压应是3维,300*300=90000,如果以一般数组大小(个人理解是1000000~20000000)的话大约还能压100~200左右,那么就是256——2^8了,2^8有什么实际意义呢8就是k的最小值,我们就可以根据这个信息猜到可能是要去压8位。
那么压8位的化状态数组就是f[301][301][1<<8]了,f[i][j][s]就表示从i到j之间进行字符串替换后成为s的状态所能得到的最大值,转移也就成为了区间动归套路,首先第一层循环一定是枚举区间长度,用小区间去维护大区间,第二层就是枚举左端点了,第三层也就是枚举中间值,由于还有状压,第四层就是枚举状态了,由于我们每次合并都是以k为长度,所以右端点不必挨个枚举,每次减去k-1即可。
然后如果len=k-1,说明该串可以被合并,我们就去找每个合法状态中转化为0或1中最大的两个,由于最终只能变化为0或1,这样无疑是正确的,然后,统计一下最优答案就好了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<iostream> 2 #include<cstdlib> 3 #include<cstdio> 4 #include<cstring> 5 #include<algorithm> 6 #include<map> 7 #include<queue> 8 #include<string> 9 #include<cmath> 10 using namespace std; 11 int n,k; 12 long long b[1<<8],c[1<<8]; 13 long long f[301][301][1<<7]; 14 char a[302]; 15 int main(){ 16 // freopen("merge.in","r",stdin); 17 // freopen("merge.out","w",stdout); 18 memset(f,-0x7f,sizeof(f)); 19 scanf("%d%d",&n,&k); 20 scanf("%s",a); 21 for(int i=1;i<=n;i++) 22 { 23 f[i][i][a[i-1]-'0']=0; 24 } 25 for(int i=0;i<(1<<k);i++) 26 { 27 scanf("%lld%lld",&b[i],&c[i]); 28 } 29 for(int l=2;l<=n;l++) 30 { 31 for(int i=1;i<=n-l+1;i++) 32 { 33 int j=i+l-1; 34 int len=j-i; 35 while(len>=k){ 36 len-=k-1; 37 } 38 for(int t=j;t>i;t-=k-1) 39 { 40 for(int s=0;s<(1<<(len));s++) 41 { 42 if(f[i][t-1][s]!=f[0][0][0]&&f[t][j][0]!=f[0][0][0]) 43 { 44 f[i][j][s<<1]=max(f[i][j][s<<1],f[i][t-1][s]+f[t][j][0]); 45 } 46 if(f[i][t-1][s]!=f[0][0][0]&&f[t][j][1]!=f[0][0][0]) 47 { 48 f[i][j][s<<1|1]=max(f[i][j][s<<1|1],f[i][t-1][s]+f[t][j][1]); 49 } 50 } 51 } 52 if(len==k-1) 53 { 54 long long g[2]; 55 g[0]=g[1]=f[0][0][0]; 56 for(int s=0;s<(1<<k);s++) 57 { 58 if(f[i][j][s]!=f[0][0][0]) 59 { 60 g[b[s]]=max(g[b[s]],f[i][j][s]+c[s]); 61 } 62 } 63 f[i][j][1]=g[1]; 64 f[i][j][0]=g[0]; 65 } 66 } 67 } 68 long long ans=f[0][0][0]; 69 for(int i=0;i<(1<<k);i++) 70 { 71 ans=max(ans,f[1][n][i]); 72 } 73 printf("%lld\n",ans); 74 //while(1); 75 return 0; 76 }