[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,这样无疑是正确的,然后,统计一下最优答案就好了。
  
 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
",ans);
74     //while(1);
75     return 0;
76 }
View Code


 
原文地址:https://www.cnblogs.com/liutianrui/p/7371330.html