• group 状压dp


      应某些人要求,我把标签删掉了

      这是一道好题。

      一看$c<=16$果断状压,但是怎么压?

      一个很显然的思路是,枚举上下两层的状态,每一层的状态极限有$C(c,c/2)$,c=16的时候有13000左右,显然是死掉了。

      我们考虑换个角度。上下两层的状态数太多,那我们不妨只考虑一层,而每个点只与它上下左右四个点有关,在dp的时候也只需要考虑上面和左边的数,多余的点在转移完右边和下边之后就失去了用处,那么我们不妨扔掉它们。

      想到这个之后这道题就比较简单了。

      我们令$f[i][j][k]$表示当前考虑第i行第j个位置,状态为k时候的状态数,转移思路和插头dp有些类似,考虑当前格上面和左边是否有字母转移即可

      这道题很多思路都和插头dp有些相近的地方。

      理论复杂度$O(rc2^c)$,实际则远远达不到(达到了复杂度也是对的)

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 int r,c,cur,la,ans;
     4 char s[130][55];
     5 struct hash_map{
     6     int fi[23333],ne[6000005];
     7     int val[6000006],tot,f[6000005];
     8     inline void clear(){
     9         tot=0;memset(fi,0,sizeof(fi));
    10     }
    11     inline int &operator [](int x){
    12         int y=x%23333,i=fi[y];
    13         for(;i&&val[i]!=x;i=ne[i]);
    14         if(!i) ne[++tot]=fi[y],fi[y]=i=tot,val[tot]=x,f[i]=0;
    15         return f[i];
    16     }
    17 }g[2];
    18 inline int count(int x,int y){
    19     int cnt=0;
    20     for(int i=0;i<y;i++) cnt+=((x&2)>>1),x>>=1;
    21     return cnt;
    22 }
    23 inline int count2(int x,int y){
    24     int cnt=0;
    25     for(int i=c;i>=y;i--)
    26         if(x&(1<<i)) cnt++;
    27     return cnt;
    28 }
    29 int main(){
    30     cin>>r>>c;
    31     for(int i=1;i<=r;i++) scanf("%s",s[i]+1);
    32     g[0][0]=0;
    33     for(int i=1;i<=r;i++){
    34         int len=strlen(s[i]+1),lea=strlen(s[i-1]+1);
    35         for(int j=1;j<=c;j++){
    36             la=cur,cur^=1;g[cur].clear();
    37             for(int k=1;k<=g[la].tot;k++){
    38                 int v=g[la].val[k],f=g[la].f[k],c1=count(v,j-1),c2=lea-count2(v,j)+1;
    39                 if(len-c1>c-j+1)continue;
    40                 if((v&(1<<j-1))&&(v&(1<<j))&&c1<len) g[cur][v]=max(g[cur][v],f+(s[i][c1+1]==s[i][c1])+(s[i-1][c2]==s[i][c1+1]));
    41                 else if(v&(1<<j-1)&&c1<len) g[cur][v|(1<<j)]=max(g[cur][v|(1<<j)],f+(s[i][c1+1]==s[i][c1]));
    42                 else if(v&(1<<j)&&c1<len) g[cur][v]=max(g[cur][v],f+(s[i][c1+1]==s[i-1][c2]));
    43                 else if(c1<len) g[cur][v|(1<<j)]=max(g[cur][v|(1<<j)],f);
    44                 if(len-c1<=c-j)g[cur][(v|(1<<j))^(1<<j)]=max(g[cur][(v|(1<<j))^(1<<j)],f);
    45             }
    46         }
    47     }
    48     for(int i=1;i<=g[cur].tot;i++)
    49         if(count(g[cur].val[i],c)==strlen(s[r]+1))
    50             ans=max(ans,g[cur].f[i]);
    51     printf("%d
    ",ans<<1);
    52     return 0;
    53 }
  • 相关阅读:
    启动控制面板命令大全
    C#下载网页为mht文件
    基于C#语言的可编程表达式计算器设计
    FileSystemWatcher监视文件变动
    C#梁朝伟变刘德华之山寨实现
    Json之语法,格式
    Regex类
    C# 索引器
    优化正则表达式的诀窍
    正则表达式(二) 零宽断言与懒惰匹配以及平衡组
  • 原文地址:https://www.cnblogs.com/hzoi-cbx/p/11566865.html
Copyright © 2020-2023  润新知