2017.8.15 [Haoi2016]字符合并 区间dp+状压dp

【题目描述】

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

【输入格式】

第一行两个整数n,k

接下来一行长度为n01串,表示初始串。输入的的相邻字符之间用一个空格隔开。

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

【输出格式】

输出一个整数表示答案。

【样例输入】

3 2
1 0 1
1 10
1 10
0 20
1 30

【样例输出】

40

【样例说明】

第3行到第6行表示长度为2401串合并方案。00->1,得10分,01->110分,10->020分,11->130分。

【数据范围】

对于100%的数据,n1,0i1,wi1

solution

考试compling error 原因是 int temp,;    ......  虽然编译过也只有15分,打的暴搜

正解是 区间dp+状压dp

f[i][j][k]    i~j这个区间消到k状态时的最大得分   设 P为这个区间消到最后的长度

枚举Len(区间长度) l(左端点) r(右端点) mid(中间点,l~mid-1是最后消到长度为P-1,mid~r为 n(k-1)+1,因为这样mid~r正好可以消到长度为1) k ( 长度len-1的状态 )

mid要倒着枚举,如果P==len可以再用 f[i][j][0~(1<<k)-1] 更新 f[i][j][0]和f[i][j][1]

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 #define ll long long
 5 #define mem(a,b) memset(a,b,sizeof(a))
 6 using namespace std;
 7 const int N=(1<<8)+50;
 8 int read()
 9 {
10     char q=getchar();
11     while(q!='1'&&q!='0')q=getchar();
12     return q-'0';
13 }
14 
15 int n,k,maxp;
16 int u,o;
17 int a[306];
18 ll f[306][306][N],c[N],w[N];
19 ll g[2],ans;
20 
21 int get(int l,int r)
22 {
23     int temp=0;
24     for(int i=l;i<=r;++i)
25       temp|=(a[i]<<(r-i));
26     return temp;
27 }
28 
29 int main(){
30     
31     //freopen("merge.in","r",stdin);
32     //freopen("merge.out","w",stdout);
33     
34     scanf("%d%d",&n,&k);
35     for(int i=1;i<=n;++i)
36       a[i]=read();
37     maxp=(1<<k)-1;
38     for(int i=0;i<=maxp;++i)
39         scanf("%lld%lld",&c[i],&w[i]);
40     
41     mem(f,-60);
42     int qqq=f[0][0][0];
43     for(int i=1;i<=n;++i)
44       f[i][i][a[i]]=0;
45     
46     for(int l=2;l<=n;++l)
47     {
48         int q1=n-l+1;
49         for(int i=1;i<=q1;++i)
50         {
51             int j=i+l-1;
52             int len=j-i;
53             while(len>=k)
54               len-=(k-1);//在求合并之后的长度 
55             
56             for(int d=j;d>=i;d-=(k-1))
57                 for(int p=0;p<(1<<len);++p)
58                     if(f[i][d-1][p]!=qqq)
59                     {
60                         if(f[d][j][0]!=qqq)
61                           f[i][j][p<<1]=max(f[i][j][p<<1],f[i][d-1][p]+f[d][j][0]);//枚举右边是 nk+(k-1),这样整k个的合并 
62                       if(f[d][j][1]!=qqq)
63                             f[i][j][p<<1|1]=max(f[i][j][p<<1|1],f[i][d-1][p]+f[d][j][1]);
64                     }
65             
66             if(len==k-1)
67             {
68                 g[0]=f[i][j][0];
69                 g[1]=f[i][j][1];
70                 
71                 for(int p=0;p<=maxp;++p)
72                   if(f[i][j][p]!=qqq)
73                     g[c[p]]=max(g[c[p]],f[i][j][p]+w[p]);
74             
75                 f[i][j][0]=g[0];
76                 f[i][j][1]=g[1];
77             }
78             
79         }
80     }
81     
82     for(int i=0;i<=maxp;++i)
83       ans=max(ans,f[1][n][i]);
84     cout<<ans;
85     //while(1);
86     return 0;
87 }
code
原文地址:https://www.cnblogs.com/A-LEAF/p/7366884.html