• [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
    
    
    
     
  • 相关阅读:
    hdu 1250
    hdu 4540(简单dp)
    hdu 1078+hdu1978+hdu 1428
    hdu 2208(dfs)
    hdu 3639(强连通+缩点+建反向图)+hdu 3072(最小树形图)
    hdu 1317+hdu 1535(SPFA)
    hdu 1245(最短路+bfs)
    hdu 1286( 欧拉函数 )
    Elementary Methods in Number Theory Exercise 1.4.1
    Elementary Methods in Number Theory Exercise 1.4.2
  • 原文地址:https://www.cnblogs.com/liutianrui/p/7371330.html
Copyright © 2020-2023  润新知