• 插头DP专题


    建议入门的人先看cd琦的《基于连通性状态压缩的动态规划问题》。事半功倍。

    插头DP其实是比较久以前听说的一个东西,当初是水了几道水题,最近打算温习一下,顺便看下能否入门之类。

    插头DP建议先理解“插头”的概念。然后会HASH表(这个其实是很基础的东西,应该都会的)。然后就是DP。

    以及特殊题目的特殊处理。

    好像一般是求N,M<=12的网格图的某种回路数或某种通路数的方案数。

    大体上每个题说几句特殊处理,有问题请纠正。。。。题目的顺序基本上难度递增

    另外代码我都是用括号匹配的。因为感觉连通块编号不好。(欢迎指教讨论)

    有一点想特别提出来的,插头DP的时候,当left==2 && up==1,即在i,j,cur的括号匹配为()时,说明回路闭合。这点在一些题里都有体现。

    注意:

    一、多条回路的题一般可以用1位表示有无插头。

    二、单条回路的题,闭合时要判断是否合法。

    三、单条通路的题,个人建议特判。

    四、转移过程中,要记录特殊的限制条件。

    五、有待补充

    HDU 1693 - Eat the Trees

    给一个网格图,有必须走和不可走2种类型。必须走的必须全部都走完且仅走一次。输出哈密顿回路(可多条回路)的方案数。

    题解:用1位来表示有无插头即可,因为这题是可以多回路,所以没必要记录括号类型或者是连通块编号。我用表示括号类型或表示连通块编号的时候TLE。。。

    其实这题不需要记录括号类型、连通块编号。所以其实可以不用这么冗长的代码。直接dp也可。

      1 #include <cstdio>
      2 #include <cstring>
      3 #include <iostream>
      4 using namespace std;
      5 
      6 #define ll long long
      7 #define maxd 15
      8 #define HASH 10007
      9 #define STATE 500010
     10 
     11 int maze[maxd][maxd],code[maxd];
     12 int n,m;
     13 struct HASHMAP{
     14     int head[HASH];
     15     int state[STATE],nxt[STATE];
     16     ll f[STATE];
     17     int sz;
     18     void clear(){sz=0;memset(head,-1,sizeof(head));}
     19     void push(int st,ll ans){
     20         int h=st%HASH;
     21         for(int i=head[h];i!=-1;i=nxt[i]){
     22             if(state[i]==st){
     23                 f[i]+=ans;
     24                 return ;
     25             }
     26         }
     27         state[sz]=st,nxt[sz]=head[h],f[sz]=ans;
     28         head[h]=sz++;
     29     }
     30 }hm[2];
     31 void decode(int st){
     32     for(int i=m;i>=0;--i) code[i]=st&1,st>>=1;
     33 }
     34 int encode(){
     35     int ret=0;
     36     for(int i=0;i<=m;++i) ret = ret<<1|code[i];
     37     return ret;
     38 }
     39 void shift(){
     40     for(int i=m;i;--i) code[i]=code[i-1];
     41     code[0]=0;
     42 }
     43 void dpblock(int i,int j,int cur){
     44     int mv = (j==m?1:0);
     45     int tmp=(1<<((m+1)-mv))-1;
     46     for(int k=0;k<hm[cur].sz;++k)
     47         hm[cur^1].push((hm[cur].state[k]>>mv)&tmp, hm[cur].f[k]);
     48 }
     49 void dpblank(int i,int j,int cur){
     50     for(int k=0;k<hm[cur].sz;++k){
     51         decode(hm[cur].state[k]);
     52         int left = code[j-1], up = code[j];
     53         if(left && up){
     54             code[j-1]=code[j]=0;
     55             if(j==m) shift();
     56             hm[cur^1].push(encode(),hm[cur].f[k]);
     57         }else if(left || up){
     58             if(i+1<=n && maze[i+1][j]==1){
     59                 code[j-1]=1,code[j]=0;
     60                 if(j==m)shift();
     61                 hm[cur^1].push(encode(),hm[cur].f[k]);
     62             }
     63             if(j+1<=m && maze[i][j+1]==1){
     64                 code[j-1]=0,code[j]=1;
     65                 hm[cur^1].push(encode(),hm[cur].f[k]);
     66             }
     67         }else {
     68             if(i+1<=n && j+1<=m && maze[i+1][j]==1 && maze[i][j+1]==1){
     69                 code[j-1]=code[j]=1;
     70                 hm[cur^1].push(encode(),hm[cur].f[k]);
     71             }
     72         }
     73     }
     74 }
     75 void solve(){
     76     int cur=0;
     77     hm[0].clear();
     78     hm[0].push(0,1);
     79     for(int i=1;i<=n;++i){
     80         for(int j=1;j<=m;++j){
     81             hm[cur^1].clear();
     82             if(maze[i][j]==0) dpblock(i,j,cur);
     83             else dpblank(i,j,cur);
     84             cur^=1;
     85         }
     86     }
     87     ll ans=0;
     88     for(int k=0;k<hm[cur].sz;++k) ans+=hm[cur].f[k];
     89     printf("There are %I64d ways to eat the trees.
    ",ans);
     90 }
     91 int main(){
     92     int t,ca=0;
     93     scanf("%d",&t);
     94     while(t--){
     95         scanf("%d%d",&n,&m);
     96         for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)scanf("%d",maze[i]+j);
     97         printf("Case %d: ",++ca);
     98         solve();
     99     }
    100     return 0;
    101 }
    HDU 1693

    URAL 1519 - Formula 1

    给一个网格图,有必须走和不可走2种类型。必须走的必须全部都走完且仅走一次。输出哈密顿回路(单条回路)的方案数。

    题解:用2位表示括号类型,闭合时判断是否为合法闭合处(最后一个可走的格子)。

      1 #include <cstdio>
      2 #include <cstring>
      3 #include <iostream>
      4 using namespace std;
      5 
      6 #define ll long long
      7 #define maxd 15
      8 #define HASH 10007
      9 #define STATE 500010
     10 char maze[maxd][maxd];
     11 int code[maxd];
     12 int n,m;
     13 int ox,oy;
     14 struct HASHMAP{
     15     int head[HASH];
     16     int state[STATE],nxt[STATE];
     17     ll f[STATE];
     18     int sz;
     19     void clear(){sz=0;memset(head,-1,sizeof(head));}
     20     void push(int st,ll ans){
     21         int h=st%HASH;
     22         for(int i=head[h];i!=-1;i=nxt[i]){
     23             if(st==state[i]){
     24                 f[i]+=ans;
     25                 return ;
     26             }
     27         }
     28         state[sz]=st,nxt[sz]=head[h],f[sz]=ans;
     29         head[h]=sz++;
     30     }
     31 }hm[2];
     32 void decode(int st){
     33     for(int i=m;i>=0;--i)code[i]=st&3,st>>=2;
     34 }
     35 int encode(){
     36     int ret=0;
     37     for(int i=0;i<=m;++i)ret = ret<<2|code[i];
     38     return ret;
     39 }
     40 void shift(){
     41     for(int i=m;i;--i)code[i]=code[i-1];
     42     code[0]=0;
     43 }
     44 void dpblock(int i,int j,int cur){
     45     int mv = (j==m?2:0);
     46     int tmp=(1<<(2*(m+1)-mv))-1;
     47     for(int k=0;k<hm[cur].sz;++k)
     48         hm[cur^1].push((hm[cur].state[k]>>mv)&tmp, hm[cur].f[k]);
     49 }
     50 ll ans;
     51 void dpblank(int i,int j,int cur){
     52     for(int k=0;k<hm[cur].sz;++k){
     53         decode(hm[cur].state[k]);
     54         int left = code[j-1], up = code[j];
     55         if(left && up){
     56             if(left==2&&up==1){
     57                 if(i!=ox||j!=oy)continue;
     58                 code[j-1]=code[j]=0;
     59                 ans+=hm[cur].f[k];
     60                 continue;
     61             }else if(left==2&&up==2){
     62                 code[j-1]=code[j]=0;
     63                 for(int jj=j+1,tmp=1;jj<=m;++jj){
     64                     if(code[jj]==2)++tmp;
     65                     if(code[jj]==1)--tmp;
     66                     if(tmp==0){code[jj]=3-code[jj];break;}
     67                 }
     68             }else if(left==1&&up==1){
     69                 code[j-1]=code[j]=0;
     70                 for(int jj=j-2,tmp=1;jj>=0;--jj){
     71                     if(code[jj]==1)++tmp;
     72                     if(code[jj]==2)--tmp;
     73                     if(tmp==0){code[jj]=3-code[jj];break;}
     74                 }
     75             }else {
     76                 code[j-1]=code[j]=0;
     77             }
     78             if(j==m)shift();
     79             hm[cur^1].push(encode(),hm[cur].f[k]);
     80         }
     81         else if(left || up){
     82             int t = left|up;
     83             if(i+1<=n && maze[i+1][j]=='.'){
     84                 code[j-1]=t,code[j]=0;
     85                 if(j==m)shift();
     86                 hm[cur^1].push(encode(),hm[cur].f[k]);
     87             }
     88             if(j+1<=m && maze[i][j+1]=='.'){
     89                 code[j-1]=0,code[j]=t;
     90                 hm[cur^1].push(encode(),hm[cur].f[k]);
     91             }
     92         }
     93         else {
     94             if(i+1<=n && maze[i+1][j]=='.' && j+1<=m && maze[i][j+1]=='.'){
     95                 code[j-1]=2,code[j]=1;
     96                 hm[cur^1].push(encode(),hm[cur].f[k]);
     97             }
     98         }
     99     }
    100 }
    101 void solve(){
    102     int cur=0;
    103     hm[0].clear();
    104     hm[0].push(0,1);
    105     ans=0;
    106     for(int i=1;i<=n;++i){
    107         for(int j=1;j<=m;++j){
    108             hm[cur^1].clear();
    109             if(maze[i][j]=='*')dpblock(i,j,cur);
    110             else dpblank(i,j,cur);
    111             cur^=1;
    112         }
    113     }
    114     printf("%I64d
    ",ans);
    115 }
    116 int main(){
    117     while(~scanf("%d%d",&n,&m)){
    118         for(int i=1;i<=n;++i)scanf("%s",maze[i]+1);
    119         ox=oy=-1;
    120         for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)if(maze[i][j]=='.')ox=i,oy=j;
    121         if(ox==-1){puts("0");continue;}
    122         solve();
    123     }
    124     return 0;
    125 }
    URAL 1519

    FZU 1977 - Pandora adventure

    给一个网格图,有1.可走可不走 2.不可走 3.必须走 3种类型。要求必须走的必须全部都走完且仅走一次,可走的格子最多经过一次。输出回路(单条回路)的方案数。

    题解:用2位表示括号类型,将可走可不走和必须走的归为一类,可走可不走只有当没有左插且没有上插的时候,才有机会变成不走的情况,其他情况都是必须走(因为要把插头搞掉)。闭合时判断是否为合法闭合处(必须走的格子都出现完了,即当前格子序>=最后一个必须走的格子的格子序)。

      1 #include <cstdio>
      2 #include <cstring>
      3 #include <iostream>
      4 using namespace std;
      5 
      6 #define ll long long
      7 #define maxd 15
      8 #define HASH 10007
      9 #define STATE 500010
     10 
     11 char maze[maxd][maxd];
     12 int code[maxd];
     13 int n,m;
     14 int ox,oy;
     15 struct HASHMAP{
     16     int head[HASH];
     17     int state[STATE],nxt[STATE];
     18     ll f[STATE];
     19     int sz;
     20     void clear(){sz=0;memset(head,-1,sizeof(head));}
     21     void push(int st,ll ans){
     22         int h=st%HASH;
     23         for(int i=head[h];i!=-1;i=nxt[i]){
     24             if(st==state[i]){
     25                 f[i]+=ans;
     26                 return ;
     27             }
     28         }
     29         state[sz]=st,nxt[sz]=head[h],f[sz]=ans;
     30         head[h]=sz++;
     31     }
     32 }hm[2];
     33 void decode(int st){
     34     for(int i=m;i>=0;--i)code[i]=st&3,st>>=2;
     35 }
     36 int encode(){
     37     int ret=0;
     38     for(int i=0;i<=m;++i)ret = ret<<2|code[i];
     39     return ret;
     40 }
     41 void shift(){
     42     for(int i=m;i;--i)code[i]=code[i-1];
     43     code[0]=0;
     44 }
     45 void dpblock(int i,int j,int cur){// 'X'
     46     int mv = (j==m?2:0);
     47     int tmp=(1<<(2*(m+1)-mv))-1;
     48     for(int k=0;k<hm[cur].sz;++k)
     49         hm[cur^1].push((hm[cur].state[k]>>mv)&tmp, hm[cur].f[k]);
     50 }
     51 ll ans;
     52 void dpblank(int i,int j,int cur){// '*' or 'O'
     53     for(int k=0;k<hm[cur].sz;++k){
     54         decode(hm[cur].state[k]);
     55         int left = code[j-1], up = code[j];
     56         if(left && up){
     57             if(left==2&&up==1){
     58                 code[j-1]=code[j]=0;
     59                 if(encode()==0){
     60                     if((i==ox && j>=oy) || i>ox)
     61                         ans += hm[cur].f[k];
     62                 }
     63                 continue;
     64             }else if(left==2&&up==2){
     65                 code[j-1]=code[j]=0;
     66                 for(int jj=j+1,tmp=1;jj<=m;++jj){
     67                     if(code[jj]==2)++tmp;
     68                     if(code[jj]==1)--tmp;
     69                     if(tmp==0){code[jj]=3-code[jj];break;}
     70                 }
     71             }else if(left==1&&up==1){
     72                 code[j-1]=code[j]=0;
     73                 for(int jj=j-2,tmp=1;jj>=0;--jj){
     74                     if(code[jj]==1)++tmp;
     75                     if(code[jj]==2)--tmp;
     76                     if(tmp==0){code[jj]=3-code[jj];break;}
     77                 }
     78             }else {
     79                 code[j-1]=code[j]=0;
     80             }
     81             if(j==m)shift();
     82             hm[cur^1].push(encode(),hm[cur].f[k]);
     83         }else if(left || up){
     84             int t = left|up;
     85             if(i+1<=n && maze[i+1][j]!='X'){
     86                 code[j-1]=t,code[j]=0;
     87                 if(j==m)shift();
     88                 hm[cur^1].push(encode(),hm[cur].f[k]);
     89             }
     90             if(j+1<=m && maze[i][j+1]!='X'){
     91                 code[j-1]=0,code[j]=t;
     92                 hm[cur^1].push(encode(),hm[cur].f[k]);
     93             }
     94         }else {
     95             if(maze[i][j]=='*'){// empty
     96                 if(j==m)shift();
     97                 hm[cur^1].push(encode(),hm[cur].f[k]);
     98             }
     99             if(i+1<=n && maze[i+1][j]!='X' && j+1<=m && maze[i][j+1]!='X'){// fill
    100                 code[j-1]=2,code[j]=1;
    101                 hm[cur^1].push(encode(),hm[cur].f[k]);
    102             }
    103         }
    104     }
    105 }
    106 ll solve(){
    107     int cur=0;
    108     hm[0].clear();
    109     hm[0].push(0,1);
    110     ans=0;
    111     for(int i=1;i<=n;++i){
    112         for(int j=1;j<=m;++j){
    113             hm[cur^1].clear();
    114             if(maze[i][j]!='X')dpblank(i,j,cur);
    115             if(maze[i][j]=='X')dpblock(i,j,cur);
    116             cur^=1;
    117         }
    118     }
    119     return ans;
    120 }
    121 int main(){
    122     int t,ca=0;
    123     scanf("%d",&t);
    124     while(t--){
    125         scanf("%d%d",&n,&m);
    126         for(int i=1;i<=n;++i)scanf("%s",maze[i]+1);
    127         ox=oy=n+1;
    128         for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)if(maze[i][j]=='O')ox=i,oy=j;
    129         printf("Case %d: %I64d
    ",++ca,ox==n+1?0:solve());
    130     }
    131     return 0;
    132 }
    FZU 1977

    HDU 1964 - Pipes

    给一个网格图,只有1种类型的格子,格子之间有边权,要求用一条回路遍历所有的格子仅一次。输出最小边权和(单条回路)。

    题解:用2位表示括号类型,读入数据的时候我是用getchar读的。这题可以不用写dpblock。wall[i][j][0],wall[i][j][1]分别表示格子(i,j)的右边的边权、下边的边权。闭合时判断是否为合法闭合处(i==n&&j==m)。

      1 #include <cstdio>
      2 #include <cstring>
      3 #include <iostream>
      4 using namespace std;
      5 
      6 #define ll long long
      7 #define maxd 15
      8 #define HASH 10007
      9 #define STATE 500010
     10 #define inf 0x3f3f3f3f
     11 
     12 int wall[maxd][maxd][2];
     13 int n,m;
     14 int code[maxd];
     15 struct HASHMAP{
     16     int head[HASH];
     17     int state[STATE],nxt[STATE];
     18     int f[STATE];
     19     int sz;
     20     void clear(){sz=0;memset(head,-1,sizeof(head));}
     21     void push(int st,int ans){
     22         int h = st%HASH;
     23         for(int i=head[h];i!=-1;i=nxt[i]){
     24             if(st==state[i]){
     25                 f[i] = min(f[i], ans);
     26                 return ;
     27             }
     28         }
     29         state[sz]=st,nxt[sz]=head[h],f[sz]=ans;
     30         head[h]=sz++;
     31     }
     32 }hm[2];
     33 void decode(int st){
     34     for(int i=m;i>=0;--i)code[i]=st&3,st>>=2;
     35 }
     36 int encode(){
     37     int ret=0;
     38     for(int i=0;i<=m;++i)ret=ret<<2|code[i];
     39     return ret;
     40 }
     41 void shift(){
     42     for(int i=m;i;--i)code[i]=code[i-1];
     43     code[0]=0;
     44 }
     45 void dpblank(int i,int j,int cur){
     46     for(int k=0;k<hm[cur].sz;++k){
     47         decode(hm[cur].state[k]);
     48         int left = code[j-1], up = code[j];
     49         if(left&&up){
     50             if(left==2&&up==1){
     51                 code[j-1]=code[j]=0;
     52                 if(i!=n||j!=m)continue;
     53                 shift();
     54                 hm[cur^1].push(encode(),hm[cur].f[k]);
     55             }else if(left==2&&up==2){
     56                 code[j-1]=code[j]=0;
     57                 for(int jj=j+1,tmp=1;jj<=m;++jj){
     58                     if(code[jj]==2)++tmp;
     59                     if(code[jj]==1)--tmp;
     60                     if(tmp==0){code[jj]=3-code[jj];break;}
     61                 }
     62             }else if(left==1&&up==1){
     63                 code[j-1]=code[j]=0;
     64                 for(int jj=j-2,tmp=1;jj>=0;--jj){
     65                     if(code[jj]==1)++tmp;
     66                     if(code[jj]==2)--tmp;
     67                     if(tmp==0){code[jj]=3-code[jj];break;}
     68                 }
     69             }else {
     70                 code[j-1]=code[j]=0;
     71             }
     72             if(j==m)shift();
     73             hm[cur^1].push(encode(), hm[cur].f[k]);
     74         }else if(left||up){
     75             int t = left|up;
     76             if(i+1<=n){
     77                 code[j-1]=t,code[j]=0;
     78                 if(j==m)shift();
     79                 hm[cur^1].push(encode(), hm[cur].f[k]+wall[i][j][1]);
     80             }
     81             if(j+1<=m){
     82                 code[j-1]=0,code[j]=t;
     83                 hm[cur^1].push(encode(), hm[cur].f[k]+wall[i][j][0]);
     84             }
     85         }else {
     86             if(i+1<=n && j+1<=m){
     87                 code[j-1]=2,code[j]=1;
     88                 hm[cur^1].push(encode(), hm[cur].f[k]+wall[i][j][0]+wall[i][j][1]);
     89             }
     90         }
     91     }
     92 }
     93 int solve(){
     94     int cur=0;
     95     hm[0].clear();
     96     hm[0].push(0,0);
     97     for(int i=1;i<=n;++i){
     98         for(int j=1;j<=m;++j){
     99             hm[cur^1].clear();
    100             dpblank(i,j,cur);
    101             cur^=1;
    102         }
    103     }
    104     int ans=inf;
    105     for(int k=0;k<hm[cur].sz;++k)
    106         ans = min(ans, hm[cur].f[k]);
    107     return ans;
    108 }
    109 int main(){
    110     int t;
    111     scanf("%d",&t);
    112     while(t--){
    113         scanf("%d%d",&n,&m);getchar();
    114         memset(wall,0,sizeof(wall));
    115         char s[22];
    116         scanf("%s",s);
    117         char cc;
    118         cc=getchar();// '
    '
    119         for(int i=1;i<=n;++i){
    120             cc=getchar();// '#'
    121             for(int j=1;j<m;++j){
    122                 cc=getchar();// ' '
    123                 cc=getchar();
    124                 wall[i][j][0]=cc-'0';
    125             }
    126             cc=getchar();// ' '
    127             cc=getchar();// '#'
    128             cc=getchar();// '
    '
    129             if(i==n){scanf("%s",s);break;}
    130             cc=getchar();// '#'
    131             for(int j=1;j<=m;++j){
    132                 cc=getchar();
    133                 wall[i][j][1]=cc-'0';
    134                 cc=getchar();// '#'
    135             }
    136             cc=getchar();// '
    '
    137         }
    138         printf("%d
    ",solve());
    139     }
    140     return 0;
    141 }
    HDU 1964

    HDU 3377 - Plan

    给一个网格图,只有1种类型的格子,格子有点权,现从左上角走到右下角,每个格子最多走一次,可以不走完所有格子。输出最大点权和(单条通路)。

    题解:用2位表示括号类型,这题也不用写dpblock。转移过程中,要保证不闭合(left==2&&up==1时直接continue)。右下角(终点)特判有且仅有一个方向即可。

      1 #include <cstdio>
      2 #include <cstring>
      3 #include <iostream>
      4 using namespace std;
      5 
      6 #define ll long long
      7 #define maxd 15
      8 #define HASH 10007
      9 #define STATE 500010
     10 #define inf 0x3f3f3f3f
     11 
     12 int n,m;
     13 int code[maxd];
     14 int val[maxd][maxd];
     15 struct HASHMAP{
     16     int head[HASH];
     17     int state[STATE],nxt[STATE];
     18     int f[STATE];
     19     int sz;
     20     void clear(){sz=0;memset(head,-1,sizeof(head));}
     21     void push(int st,int ans){
     22         int h = st%HASH;
     23         for(int i=head[h];i!=-1;i=nxt[i]){
     24             if(st==state[i]){
     25                 f[i]=max(f[i], ans);
     26                 return ;
     27             }
     28         }
     29         state[sz]=st,nxt[sz]=head[h],f[sz]=ans;
     30         head[h]=sz++;
     31     }
     32 }hm[2];
     33 void decode(int st){
     34     for(int i=m;i>=0;--i)code[i]=st&3,st>>=2;
     35 }
     36 int encode(){
     37     int ret=0;
     38     for(int i=0;i<=m;++i)ret=ret<<2|code[i];
     39     return ret;
     40 }
     41 void shift(){
     42     for(int i=m;i;--i)code[i]=code[i-1];
     43     code[0]=0;
     44 }
     45 int ans;
     46 void dpblank(int i,int j,int cur){
     47     for(int k=0;k<hm[cur].sz;++k){
     48         decode(hm[cur].state[k]);
     49         int left = code[j-1], up = code[j];
     50         if(i==n&&j==m){
     51             if((left || up) && !(left&&up))
     52                 ans=max(ans, hm[cur].f[k]+val[i][j]);
     53             continue;
     54         }
     55         if(left && up){
     56             if(left==2&&up==1)
     57                 continue;
     58             else if(left==2&&up==2){
     59                 code[j-1]=code[j]=0;
     60                 for(int jj=j+1,tmp=1;jj<=m;++jj){
     61                     if(code[jj]==2)++tmp;
     62                     if(code[jj]==1)--tmp;
     63                     if(tmp==0){code[jj]=3-code[jj];break;}
     64                 }
     65             }else if(left==1&&up==1){
     66                 code[j-1]=code[j]=0;
     67                 for(int jj=j-2,tmp=1;jj>=0;--jj){
     68                     if(code[jj]==1)++tmp;
     69                     if(code[jj]==2)--tmp;
     70                     if(tmp==0){code[jj]=3-code[jj];break;}
     71                 }
     72             }else {
     73                 code[j-1]=code[j]=0;
     74             }
     75             if(j==m)shift();
     76             hm[cur^1].push(encode(), hm[cur].f[k]+val[i][j]);
     77         }else if(left || up){
     78             int t = left|up;
     79             if(i+1<=n){
     80                 code[j-1]=t,code[j]=0;
     81                 if(j==m)shift();
     82                 hm[cur^1].push(encode(), hm[cur].f[k]+val[i][j]);
     83             }
     84             if(j+1<=m){
     85                 code[j-1]=0,code[j]=t;
     86                 hm[cur^1].push(encode(), hm[cur].f[k]+val[i][j]);
     87             }
     88         }else {
     89             if(j==m)shift();
     90             hm[cur^1].push(encode(), hm[cur].f[k]);
     91             if(i+1<=n && j+1<=m){
     92                 code[j-1]=2,code[j]=1;
     93                 hm[cur^1].push(encode(), hm[cur].f[k]+val[i][j]);
     94             }
     95         }
     96     }
     97 }
     98 int solve(){
     99     int cur=0;
    100     hm[0].clear();
    101     hm[0].push((1<<(2*(m+1)-1)), 0);
    102     ans=-inf;
    103     for(int i=1;i<=n;++i){
    104         for(int j=1;j<=m;++j){
    105             hm[cur^1].clear();
    106             dpblank(i,j,cur);
    107             cur^=1;
    108         }
    109     }
    110     return ans;
    111 }
    112 int main(){
    113     int ca=0;
    114     while(~scanf("%d%d",&n,&m)){
    115         for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)scanf("%d",val[i]+j);
    116         printf("Case %d: %d
    ",++ca,solve());
    117     }
    118     return 0;
    119 }
    HDU 3377

     POJ 1739 - Tony's Tour

    给一个网格图,格子有必须走和不可走2种类型。必须走的必须全部都走完且仅走一次。输出从左下角走到右下角的通路(单条通路)的方案数。

    题解:用2位表示括号类型。转移过程中,要保证不闭合(left==2&&up==1时直接continue)。左下角(起点)和右下角(终点)特判处理即可。

    一般而言,求通路的题也可以扩大网格图,转换成求回路数。

      1 #include <cstdio>
      2 #include <cstring>
      3 #include <iostream>
      4 using namespace std;
      5 
      6 #define ll long long
      7 #define maxd 15
      8 #define HASH 10007
      9 #define STATE 500010
     10 
     11 int n,m;
     12 char maze[maxd][maxd];
     13 int code[maxd];
     14 struct HASHMAP{
     15     int head[HASH];
     16     int state[STATE],nxt[STATE];
     17     ll f[STATE];
     18     int sz;
     19     void clear(){sz=0;memset(head,-1,sizeof(head));}
     20     void push(int st,ll ans){
     21         int h = st%HASH;
     22         for(int i=head[h];i!=-1;i=nxt[i]){
     23             if(st==state[i]){
     24                 f[i]+=ans;
     25                 return ;
     26             }
     27         }
     28         state[sz]=st,nxt[sz]=head[h],f[sz]=ans;
     29         head[h]=sz++;
     30     }
     31 }hm[2];
     32 void decode(int st){
     33     for(int i=m;i>=0;--i)code[i]=st&3,st>>=2;
     34 }
     35 int encode(){
     36     int ret=0;
     37     for(int i=0;i<=m;++i)ret=ret<<2|code[i];
     38     return ret;
     39 }
     40 void shift(){
     41     for(int i=m;i;--i)code[i]=code[i-1];
     42     code[0]=0;
     43 }
     44 void dpblock(int i,int j,int cur){
     45     int mv = (j==m?2:0);
     46     int tmp = (1<<(2*(m+1)-mv))-1;
     47     for(int k=0;k<hm[cur].sz;++k)
     48         hm[cur^1].push((hm[cur].state[k]>>mv)&tmp, hm[cur].f[k]);
     49 }
     50 ll ans;
     51 void dpblank(int i,int j,int cur){
     52     for(int k=0;k<hm[cur].sz;++k){
     53         decode(hm[cur].state[k]);
     54         int left = code[j-1], up = code[j];
     55         if(i==n&&j==1){
     56             if(up){
     57                 code[j]=0;
     58                 hm[cur^1].push(encode(), hm[cur].f[k]);
     59             }else {
     60                 code[j]=2;
     61                 hm[cur^1].push(encode(), hm[cur].f[k]);
     62             }
     63             continue;
     64         }
     65         if(i==n&&j==m){
     66             if((left || up) && !(left&&up))
     67                 ans+=hm[cur].f[k];
     68             continue;
     69         }
     70         if(left && up){
     71             if(left==2&&up==1)continue;
     72             else if(left==2&&up==2){
     73                 code[j-1]=code[j]=0;
     74                 for(int jj=j+1,tmp=1;jj<=m;++jj){
     75                     if(code[jj]==2)++tmp;
     76                     if(code[jj]==1)--tmp;
     77                     if(tmp==0){code[jj]=3-code[jj];break;}
     78                 }
     79             }else if(left==1&&up==1){
     80                 code[j-1]=code[j]=0;
     81                 for(int jj=j-2,tmp=1;jj>=0;--jj){
     82                     if(code[jj]==1)++tmp;
     83                     if(code[jj]==2)--tmp;
     84                     if(tmp==0){code[jj]=3-code[jj];break;}
     85                 }
     86             }else {
     87                 code[j-1]=code[j]=0;
     88             }
     89             if(j==m)shift();
     90             hm[cur^1].push(encode(), hm[cur].f[k]);
     91         }else if(left || up){
     92             int t = left | up;
     93             if(i+1<=n && maze[i+1][j]=='.'){
     94                 code[j-1]=t,code[j]=0;
     95                 if(j==m)shift();
     96                 hm[cur^1].push(encode(), hm[cur].f[k]);
     97             }
     98             if(j+1<=m && maze[i][j+1]=='.'){
     99                 code[j-1]=0,code[j]=t;
    100                 hm[cur^1].push(encode(), hm[cur].f[k]);
    101             }
    102         }else {
    103             if(i+1<=n &&j+1<=m && maze[i+1][j]=='.' && maze[i][j+1]=='.'){
    104                 code[j-1]=2,code[j]=1;
    105                 hm[cur^1].push(encode(), hm[cur].f[k]);
    106             }
    107         }
    108     }
    109 }
    110 ll solve(){
    111     int cur=0;
    112     hm[0].clear();
    113     hm[0].push(0,1);
    114     ans=0;
    115     for(int i=1;i<=n;++i){
    116         for(int j=1;j<=m;++j){
    117             hm[cur^1].clear();
    118             if(maze[i][j]=='.') dpblank(i,j,cur);
    119             else dpblock(i,j,cur);
    120             cur^=1;
    121         }
    122     }
    123     return ans;
    124 }
    125 int main(){
    126     while(~scanf("%d%d",&n,&m) && n){
    127         for(int i=1;i<=n;++i)scanf("%s",maze[i]+1);
    128         if(m==1){
    129             int ans=1;
    130             for(int i=1;i<n;++i)if(maze[i][1]=='.')ans=0;
    131             if(maze[n][1]=='#')ans=0;
    132             printf("%d
    ",ans);
    133             continue;
    134         }
    135         printf("%I64d
    ",solve());
    136     }
    137     return 0;
    138 }
    POJ 1739

    POJ 3133 - Manhattan Wiring

    给一个网格图,格子有1.可走可不走 2.不可走 3.标记"2" 4.标记"3" 4种类型。且3、4类型各有两个格子。问将2和3分别连起来(2和3的线不可相交)的最小曼哈顿距离和。

    题解:用2位表示网格填充类型。00表示空,01表示"2"的线,10表示"3"的线。注意特判标记"2","3"的四个位置,具体看代码的特判部分。

      1 #include <cstdio>
      2 #include <cstring>
      3 #include <iostream>
      4 #include <algorithm>
      5 using namespace std;
      6 
      7 #define ll long long
      8 #define maxd 15
      9 #define HASH 10007
     10 #define STATE 500010
     11 #define inf 0x3f3f3f3f
     12 
     13 int n,m,maze[maxd][maxd];
     14 int code[maxd];
     15 struct HASHMAP{
     16     int head[HASH];
     17     int state[STATE],nxt[STATE];
     18     int f[STATE];
     19     int sz;
     20     void clear(){sz=0;memset(head,-1,sizeof(head));}
     21     void push(int st,int ans){
     22         int h = st%HASH;
     23         for(int i=head[h];i!=-1;i=nxt[i]){
     24             if(st==state[i]){
     25                 f[i]=min(f[i],ans);
     26                 return ;
     27             }
     28         }
     29         state[sz]=st,nxt[sz]=head[h],f[sz]=ans;
     30         head[h]=sz++;
     31     }
     32 }hm[2];
     33 void decode(int st){
     34     for(int i=m;i>=0;--i)code[i]=st&3,st>>=2;
     35 }
     36 int encode(){
     37     int ret=0;
     38     for(int i=0;i<=m;++i)ret=ret<<2|code[i];
     39     return ret;
     40 }
     41 void shift(){
     42     for(int i=m;i;--i)code[i]=code[i-1];
     43     code[0]=0;
     44 }
     45 void dpblock(int i,int j,int cur){
     46     int mv = (j==m?2:0);
     47     for(int k=0;k<hm[cur].sz;++k)
     48         hm[cur^1].push(hm[cur].state[k]>>mv, hm[cur].f[k]);
     49 }
     50 void dpblank(int i,int j,int cur){
     51     for(int k=0;k<hm[cur].sz;++k){
     52         decode(hm[cur].state[k]);
     53         int left = code[j-1], up = code[j];
     54         if(maze[i][j]>=2){
     55             if(left && up)continue;
     56             if(left || up){
     57                 int t = (maze[i][j]==2?1:2);
     58                 if(left+up!=t)continue;
     59                 code[j-1]=code[j]=0;
     60                 if(j==m)shift();
     61                 hm[cur^1].push(encode(),hm[cur].f[k]);
     62             }else {
     63                 int t = (maze[i][j]==2?1:2);
     64                 if(i+1<=n && maze[i+1][j]!=1){
     65                     code[j-1]=t,code[j]=0;
     66                     if(j==m)shift();
     67                     hm[cur^1].push(encode(),hm[cur].f[k]);
     68                 }
     69                 if(j+1<=m && maze[i][j+1]!=1){
     70                     code[j-1]=0,code[j]=t;
     71                     hm[cur^1].push(encode(),hm[cur].f[k]);
     72                 }
     73             }
     74             continue;
     75         }
     76         if(left && up){
     77             if(left!=up)continue;
     78             code[j-1]=code[j]=0;
     79             if(j==m)shift();
     80             hm[cur^1].push(encode(),hm[cur].f[k]+1);
     81         }else if(left || up){
     82             int t = left | up;
     83             if(i+1<=n && maze[i+1][j]!=1){
     84                 code[j-1]=t,code[j]=0;
     85                 if(j==m)shift();
     86                 hm[cur^1].push(encode(),hm[cur].f[k]+1);
     87             }
     88             if(j+1<=m && maze[i][j+1]!=1){
     89                 code[j-1]=0,code[j]=t;
     90                 hm[cur^1].push(encode(),hm[cur].f[k]+1);
     91             }
     92         }else {
     93             if(maze[i][j]==0){
     94                 if(j==m)shift();
     95                 hm[cur^1].push(encode(),hm[cur].f[k]);
     96             }
     97             if(i+1<=n && j+1<=m && maze[i+1][j]!=1 && maze[i][j+1]!=1){
     98                 code[j-1]=code[j]=1;
     99                 hm[cur^1].push(encode(),hm[cur].f[k]+1);
    100                 code[j-1]=code[j]=2;
    101                 hm[cur^1].push(encode(),hm[cur].f[k]+1);
    102             }
    103         }
    104     }
    105 }
    106 int solve(){
    107     int cur=0;
    108     hm[0].clear();
    109     hm[0].push(0,0);
    110     for(int i=1;i<=n;++i){
    111         for(int j=1;j<=m;++j){
    112             hm[cur^1].clear();
    113             if(maze[i][j]==1)dpblock(i,j,cur);
    114             else dpblank(i,j,cur);
    115             cur^=1;
    116         }
    117     }
    118     int ans=inf;
    119     for(int k=0;k<hm[cur].sz;++k)ans = min(ans, hm[cur].f[k]);
    120     return ans;
    121 }
    122 int main(){
    123     while(~scanf("%d%d",&n,&m) && n){
    124         for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)scanf("%d",maze[i]+j);
    125         int ans = solve();
    126         printf("%d
    ",ans==inf?0:ans+2);
    127     }
    128     return 0;
    129 }
    POJ 3133

    ============================接下来4题个人感觉比上边的难==================================================

    ZOJ 3466 - The Hive II

    给一个六边形的网格图,格子有必须走和不可走2种类型。必须走的必须全部都走完且仅走一次。输出哈密顿回路(可多条回路)的方案数。

    题解:基本思路跟HDU 1693一样。只是处理六边形比四边形麻烦得多。这题应该是可以不用HASH表,可以直接dp的。因为不需要记录括号类型、连通块编号。

    处理六边形的时候,设某个平放(上下是平,左右是凸的)六边形位置为(x,y),则相邻的六边形位置分别为(x+1,y+1),(x+1,y-1),(x-1,y+1),(x-1,y-1),(x,y+1),(x,y-1)。

    我用1位表示有无插头,总共有(2×m+1位)每次DP的时候有3个插头。1个左插,2个上插。我是一列列来DP的,注意换列的时候,奇列变偶列的时候要平移2位,偶列变奇列的时候不用平移。

    (补充示意图==1.六边形位置 2.插头位置 3.DP顺序 4.换列平移)

      1 #include <cstdio>
      2 #include <cstring>
      3 #include <iostream>
      4 using namespace std;
      5 
      6 #define ll long long
      7 #define maxd 33
      8 #define HASH 10007
      9 #define STATE 500010
     10 
     11 int n,m,code[maxd];
     12 int maze[maxd][maxd];
     13 struct HASHMAP{
     14     int head[HASH];
     15     int state[STATE],nxt[STATE];
     16     ll f[STATE];
     17     int sz;
     18     void clear(){sz=0;memset(head,-1,sizeof(head));}
     19     void push(int st,ll ans){
     20         int h = st%HASH;
     21         for(int i=head[h];i!=-1;i=nxt[i]){
     22             if(st==state[i]){
     23                 f[i]+=ans;
     24                 return ;
     25             }
     26         }
     27         state[sz]=st,nxt[sz]=head[h],f[sz]=ans;
     28         head[h]=sz++;
     29     }
     30 }hm[2];
     31 void decode(int st){
     32     for(int i=m*2;i>=0;--i)code[i]=st&1,st>>=1;
     33 }
     34 int encode(){
     35     int ret=0;
     36     for(int i=0;i<=2*m;++i)ret = ret<<1|code[i];
     37     return ret;
     38 }
     39 void shift(){
     40     for(int i=2*m;i>=2;--i)code[i]=code[i-2];
     41     code[1]=code[0]=0;
     42 }
     43 void dpblock(int i,int j,int cur){
     44     int mv = 0;
     45     if((j/2+1==m) && (i&1)) mv=2;
     46     for(int k=0;k<hm[cur].sz;++k)
     47         hm[cur^1].push(hm[cur].state[k]>>mv, hm[cur].f[k]);
     48 }
     49 void dpblank(int i,int j,int cur){
     50     for(int k=0;k<hm[cur].sz;++k){
     51         decode(hm[cur].state[k]);
     52         int jj = j/2+1;
     53         int j1 = 2*(jj-1),j2=j1+1,j3=j2+1;
     54         int left = code[j1], up1 = code[j2], up2 = code[j3];
     55         int sum = left+up1+up2;
     56         if(sum==3)continue;
     57         if(sum==2){
     58             code[j1]=code[j2]=code[j3]=0;
     59             if(jj==m && (i&1))shift();
     60             hm[cur^1].push(encode(), hm[cur].f[k]);
     61             continue;
     62         }
     63         bool down1 = (i+1<=n && j-1>=0 && maze[i+1][j-1]==0);
     64         bool down2 = (i+1<=n && j+1<2*m && maze[i+1][j+1]==0);
     65         bool right = (j+2<2*m && maze[i][j+2]==0);
     66         if(sum==1){
     67             if(down1){
     68                 code[j1]=1,code[j2]=code[j3]=0;
     69                 if(jj==m && (i&1))shift();
     70                 hm[cur^1].push(encode(), hm[cur].f[k]);
     71                 decode(hm[cur].state[k]);
     72             }
     73             if(down2){
     74                 code[j1]=0,code[j2]=1,code[j3]=0;
     75                 if(jj==m && (i&1))shift();
     76                 hm[cur^1].push(encode(), hm[cur].f[k]);
     77                 decode(hm[cur].state[k]);
     78             }
     79             if(right){
     80                 code[j1]=code[j2]=0,code[j3]=1;
     81                 hm[cur^1].push(encode(), hm[cur].f[k]);
     82             }
     83             continue;
     84         }
     85         if(right){
     86             if(down1){
     87                 code[j1]=1,code[j2]=0,code[j3]=1;
     88                 hm[cur^1].push(encode(), hm[cur].f[k]);
     89             }
     90             if(down2){
     91                 code[j1]=0,code[j2]=code[j3]=1;
     92                 hm[cur^1].push(encode(), hm[cur].f[k]);
     93             }
     94         }
     95         if(down1 && down2){
     96                 code[j1]=code[j2]=1,code[j3]=0;
     97                 if(jj==m && (i&1))shift();
     98                 hm[cur^1].push(encode(), hm[cur].f[k]);
     99         }
    100     }
    101 }
    102 ll solve(){
    103     int cur=0;
    104     hm[0].clear();
    105     hm[0].push(0,1);
    106     for(int i=1;i<=n;++i){
    107         for(int j=(i&1);j<2*m;j+=2){
    108             hm[cur^1].clear();
    109             if(maze[i][j]==1)dpblock(i,j,cur);
    110             else dpblank(i,j,cur);
    111             cur^=1;
    112         }
    113     }
    114     ll ans=0;
    115     for(int k=0;k<hm[cur].sz;++k) ans += hm[cur].f[k];
    116     return ans;
    117 }
    118 int main(){
    119     int q;
    120     while(~scanf("%d%d",&m,&q)){
    121         n=8;
    122         memset(maze,0,sizeof(maze));
    123         while(q--){
    124             char tmp[4];
    125             scanf("%s",tmp);
    126             int row = tmp[1]-'A'+1;
    127             int col = tmp[0]-'A'+1;
    128             if(row&1) col = col*2 - 1;
    129             else col = col*2  - 2;
    130             maze[row][col]=1;
    131         }
    132         printf("%lld
    ",solve());
    133     }
    134     return 0;
    135 }
    ZOJ 3466

    ZOJ 3256 - Tour in the Castle

    给一个网格图,格子只有必须走1种类型。必须走的必须全部走完且仅走一次。输出从左上走到左下的哈密顿通路的方案数。网格图为N×M,其中N<=7, M<=1e9 。

    题解:注意到M很大,所以不可能按行列逐格DP。由于所有格子都是一样的,我们可以作出列与列之间的可转移关系。即某一列的上插状态(N个code[i])可推出下一列的哪些上插状态。然后构成一个矩阵A,其中A[i][j]表示第i个状态可转移到第j个状态。

    为方便描述,现将图顺时针旋转90度。一开始的状态则是最左和最右都是有1个上插。然后由这个状态推出可能出现的所有状态。

    当我们用一个邻接矩阵表示一个无权图的时候,令B=A^m,则B[i][j]表示的是i结点经过m步到达j结点的方案数。

    这题由于N<=7,所以连通块编号最多是3(1个0,2个1,2个2,2个3),所以我用了表示连通块编号的方法而没采用表示括号类型的方法,其实都一样。

    还有这题我没有用HASHMAP。因为这题在极端数据下(N=7)可达的状态也很少(不超过133,具体忘了多少了,可以试下输出那个v.size())。

    而之前我们使用HASHMAP的一个很重要的原因是,HASHMAP每次清空是O(HASH),而那些题目需要多次清空HASHMAP(一般为N×M次),而本题对于每个N只需要清空1次,所以感觉直接用STL的map和vector配合应该就可以了。

    需要注意的是,一开始我的矩阵快速幂的乘法的取模是放在最里层的,结果4520ms的AC,时限是5000ms。。好险,后来把取模放到第二层,瞬间940ms。(由于题目取模是7777777<8*1e6,所以((8*1e6)^2)*133不会爆lld)。

    至此,题目大体解决。但是我一直很怀疑,可以直接用ans==0来判断Impossible吗?如果结果刚好是MOD的倍数,被MOD成0了怎么破,是要加标记吗?

    请高手指点。。。。

    (补充示意图==1.旋转后的图 2.插头位置)

      1 #include <cstdio>
      2 #include <cstring>
      3 #include <iostream>
      4 #include <map>
      5 #include <vector>
      6 using namespace std;
      7 
      8 #define ll long long
      9 #define maxd 15
     10 #define mod 7777777
     11 #define mk 133// 阶数
     12 struct Mat{
     13     Mat(){memset(a,0,sizeof(a));}
     14     ll a[mk][mk];
     15 };
     16 Mat operator *(const Mat& a,const Mat& b){
     17     Mat ret;
     18     for(int i=0;i<mk;++i)
     19         for(int j=0;j<mk;++j){
     20             for(int k=0;k<mk;++k){
     21                 ret.a[i][j]+=a.a[i][k]*b.a[k][j];
     22                 //ret.a[i][j]%=mod;
     23             }
     24             ret.a[i][j]%=mod;
     25         }
     26     return ret;
     27 }
     28 Mat operator ^(Mat x, int n){
     29     Mat ret;
     30     for(int i=0;i<mk;++i) ret.a[i][i]=1;
     31     while(n){
     32         if(n&1)ret=ret*x;
     33         x=x*x;
     34         n>>=1;
     35     }
     36     return ret;
     37 }
     38 
     39 int n,m,code[maxd];
     40 map<int,int>mp;
     41 void decode(int st){
     42     for(int i=n;i;--i)code[i]=st&3,st>>=2;
     43 }
     44 int encode(){
     45     int cnt=0;
     46     int ch[22];
     47     memset(ch,-1,sizeof(ch));
     48     ch[0]=cnt++;
     49     int ret=0;
     50     for(int i=1;i<=n;++i){
     51         if(ch[code[i]]==-1)ch[code[i]]=cnt++;
     52         code[i]=ch[code[i]];
     53         ret = ret<<2|code[i];
     54     }
     55     return ret;
     56 }
     57 /*
     58 6种插头:
     59 0: —— or 左+上 or 右+上
     60 1: |  or 左+下 or 右+下
     61 */
     62 bool check(int st,int nst){
     63     decode(st);
     64     int left=0,cnt=0,k;
     65     for(int i=1;i<=n;++i){
     66         int nsti = nst&(1<<(n-i));
     67         if(left==0){
     68             if(code[i]==0 && nsti==0) return false;
     69             if(code[i]==0 && nsti) left = -1;// nsti: 右+下
     70             if(code[i] && nsti==0) left = code[i];// nsti: 右+上
     71             if(code[i] && nsti) continue;// nsti: |
     72             k=i;// 上一个待修改插头
     73         }
     74         else {
     75             if(code[i]==0 && nsti==0) continue;// nsti: ——
     76             else if(code[i]==0 && nsti){
     77                 if(left>0) code[k]=0,code[i]=left;// nstk: 右+上  nsti: 左+下
     78                 else code[k]=code[i]=4+(cnt++);// nstk: 右+下  nsti: 左+下
     79                 left = 0;
     80             }
     81             else if(code[i] && nsti==0){
     82                 if(code[i]==left) if(nst!=0 || i!=n) return false;// code[i]==left说明闭合。回路闭合的判断
     83                 if(left>0){// nstk: 右+上  nsti: 左+上
     84                     for(int j=1;j<=n;++j) if(code[j]==left) code[j]=code[i];
     85                     code[k]=code[i]=0;left=0;
     86                 }else {// nstk: 右+下  nsti: 左+上
     87                     code[k]=code[i],code[i]=0;left=0;
     88                 }
     89             }
     90             else if(code[i] && nsti) return false;
     91         }
     92     }
     93     return left==0;
     94 }
     95 Mat A[11];
     96 bool vis[11];
     97 void init(){
     98     if(vis[n]) return ;
     99     vis[n]=true;
    100     memset(code,0,sizeof(code));
    101     code[1]=code[n]=1;
    102     map<int,int>mp;
    103     vector<int>v;
    104     mp[0]=0;mp[encode()]=1;
    105     v.push_back(0);v.push_back(encode());
    106     int sz=2;
    107     for(int i=1;i<v.size();++i){
    108         int st = v[i], idx = i;
    109         for(int nst=0;nst<(1<<n);++nst){
    110             if(check(st,nst)){
    111                 int st2 = encode();
    112                 map<int,int>::iterator it = mp.find(st2);
    113                 if(it==mp.end()){
    114                     A[n].a[idx][sz]=1;
    115                     mp[st2] = sz++;
    116                     v.push_back(st2);
    117                 }else A[n].a[idx][it->second]=1;
    118             }
    119         }
    120     }
    121 }
    122 int main(){
    123     for(int i=0;i<11;++i)memset(A[i].a,0,sizeof(A[i].a));
    124     memset(vis,false,sizeof(vis));
    125     while(~scanf("%d%d",&n,&m)){
    126         init();
    127         Mat ans = A[n]^m;
    128         if(ans.a[1][0]==0)puts("Impossible");
    129         else printf("%d
    ",ans.a[1][0]);
    130     }
    131     return 0;
    132 }
    ZOJ 3256

    ZOJ 3213 - Beautiful Meadow

    给一个网格图,格子有可走可不走和不可走2种类型。格子有点权。输出简单路径(可以从任何一个可走可不走的格子开始或结束,每个格子最多走一次)经过格子点权和最大的点权和。

    题解:本题用3位表示连通块编号,因为存在独立插头(括号?连通块?),即头尾的单插头,即比如当一条路径为竖直时,插头状态会类似于0001000。 所以本来N,M<=7应该是连通块编号最多为3 ,但是这里可能连通块编号为4 ,所以要用3位。转移过程中,记录头尾的个数(独立插头的个数)。必须保证endcount<=2。最后的答案就是所有endcount==2(有头有尾)的f[k]的和。

    看来还是表示连通块编号的方法适用范围比较大。。。

      1 #include <cstdio>
      2 #include <cstring>
      3 #include <iostream>
      4 using namespace std;
      5 
      6 #define ll long long
      7 #define maxd 15
      8 #define HASH 10007
      9 #define STATE 500010
     10 
     11 int n,m,code[maxd],endc;
     12 int maze[maxd][maxd];
     13 struct HASHMAP{
     14     int head[HASH];
     15     int state[STATE],nxt[STATE];
     16     int f[STATE];
     17     int edc[STATE];
     18     int sz;
     19     void clear(){sz=0;memset(head,-1,sizeof(head));}
     20     void push(int st,int ans,int endc){
     21         int h = st%HASH;
     22         for(int i=head[h];i!=-1;i=nxt[i]){
     23             if(st==state[i] && endc==edc[i]){
     24                 f[i] = max(f[i], ans);
     25                 return ;
     26             }
     27         }
     28         state[sz]=st,nxt[sz]=head[h],f[sz]=ans;edc[sz]=endc;
     29         head[h]=sz++;
     30     }
     31 }hm[2];
     32 void decode(int st){
     33     for(int i=m;i>=0;--i)code[i]=st&7,st>>=3;
     34 }
     35 int encode(){
     36     int ch[maxd],sz=0,ret=0;
     37     memset(ch,-1,sizeof(ch));
     38     ch[0]=sz++;
     39     for(int i=0;i<=m;++i){
     40         if(ch[code[i]]==-1) ch[code[i]]=sz++;
     41         code[i]=ch[code[i]];
     42         ret = ret<<3|code[i];
     43     }
     44     return ret;
     45 }
     46 void shift(){
     47     for(int i=m;i;--i) code[i]=code[i-1];
     48     code[0]=0;
     49 }
     50 void dpblock(int i,int j,int cur){
     51     int mv = j==m?3:0;
     52     for(int k=0;k<hm[cur].sz;++k)
     53         hm[cur^1].push(hm[cur].state[k]>>mv, hm[cur].f[k], hm[cur].edc[k]);
     54 }
     55 int ans;
     56 void dpblank(int i,int j,int cur){
     57     for(int k=0;k<hm[cur].sz;++k){
     58         decode(hm[cur].state[k]);
     59         int left = code[j-1], up = code[j];
     60         int endc = hm[cur].edc[k];
     61         int sum=0;
     62         for(int i=0;i<=m;++i)if(code[i])++sum;
     63         if(sum+endc<=2) ans = max(ans, hm[cur].f[k]);
     64         if(endc==2 && sum==0)continue;
     65         if(left && up){
     66             if(left == up) continue;
     67             for(int jj=0;jj<=m;++jj)
     68                 if(code[jj]==left) code[jj]=up;
     69             code[j-1]=code[j]=0;
     70             if(j==m)shift();
     71             hm[cur^1].push(encode(), hm[cur].f[k]+maze[i][j], endc);
     72         }else if(left || up){
     73             int t = left | up;
     74             if(endc<2){
     75                 int tmp[maxd];memcpy(tmp,code,sizeof(tmp));
     76                 code[j-1]=code[j]=0;
     77                 if(j==m)shift();
     78                 hm[cur^1].push(encode(), hm[cur].f[k]+maze[i][j], endc+1);
     79                 memcpy(code,tmp,sizeof(tmp));
     80             }
     81             if(i+1<=n && maze[i+1][j]){
     82                 code[j-1]=t,code[j]=0;
     83                 if(j==m)shift();
     84                 hm[cur^1].push(encode(), hm[cur].f[k]+maze[i][j], endc);
     85             }
     86             if(j+1<=m && maze[i][j+1]){
     87                 code[j-1]=0,code[j]=t;
     88                 hm[cur^1].push(encode(), hm[cur].f[k]+maze[i][j], endc);
     89             }
     90         }else {
     91             if(i+1<=n && maze[i+1][j] && j+1<=m && maze[i][j+1]){
     92                 code[j-1]=code[j]=11;
     93                 hm[cur^1].push(encode(), hm[cur].f[k]+maze[i][j], endc);
     94                 code[j-1]=code[j]=0;
     95             }
     96             if(endc<2 && i+1<=n && maze[i+1][j]){
     97                 int tmp[maxd];memcpy(tmp,code,sizeof(tmp));
     98                 code[j-1]=11,code[j]=0;
     99                 if(j==m)shift();
    100                 hm[cur^1].push(encode(), hm[cur].f[k]+maze[i][j], endc+1);
    101                 memcpy(code,tmp,sizeof(tmp));
    102             }
    103             if(endc<2 && j+1<=m && maze[i][j+1]){
    104                 code[j-1]=0,code[j]=11;
    105                 hm[cur^1].push(encode(), hm[cur].f[k]+maze[i][j], endc+1);
    106                 code[j-1]=code[j]=0;
    107             }
    108             if(j==m)shift();
    109             hm[cur^1].push(encode(), hm[cur].f[k], endc);
    110         }
    111     }
    112 }
    113 int solve(){
    114     int cur=0;
    115     hm[0].clear();
    116     hm[0].push(0,0,0);
    117     ans=0;
    118     for(int i=1;i<=n;++i){
    119         for(int j=1;j<=m;++j){
    120             ans = max(ans, maze[i][j]);
    121             hm[cur^1].clear();
    122             if(maze[i][j]) dpblank(i,j,cur);
    123             else dpblock(i,j,cur);
    124             cur^=1;
    125         }
    126     }
    127     for(int k=0;k<hm[cur].sz;++k)
    128         //if(hm[cur].edc[k]==2)
    129             ans = max(ans, hm[cur].f[k]);
    130     return ans;
    131 }
    132 int main(){
    133     int t;
    134     scanf("%d",&t);
    135     while(t--){
    136         scanf("%d%d",&n,&m);
    137         for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)scanf("%d",maze[i]+j);
    138         printf("%d
    ",solve());
    139     }
    140     return 0;
    141 }
    ZOJ 3213

    HDU 4285

    给一个网格图,格子有必须走和不可走2种类型。必须走的必须全部走完且仅走一次。输出构成K个回路,且回路间不嵌套包含的方案数。

    题解:这题我用2位表示括号类型。首先,要记录当前状态构成的回路数,这个是肯定的,然后把K个回路的方案数加起来就是答案了。问题是,怎么保证不嵌套包含。这里我一开始也WA了,后来参考网上机智做法,当合并结点(i,j)的左(0~j-2)右(j+1~m)的插头个数都是偶数时,则没有嵌套包含。这个想一想还是可以想象的,不过具体证明不知道应该怎么证明了。。。然后就顺利AC了,不过数据有点大,8484+ms

      1 #include <cstdio>
      2 #include <cstring>
      3 #include <iostream>
      4 using namespace std;
      5 
      6 #define ll long long
      7 #define maxd 15
      8 #define HASH 10007
      9 #define STATE 500010
     10 #define MOD 1000000007
     11 
     12 int n,m,code[maxd];
     13 char maze[maxd][maxd];
     14 int K;
     15 struct HASHMAP{
     16     int head[HASH];
     17     int state[STATE],nxt[STATE];
     18     int f[STATE];
     19     int cnt[STATE];
     20     int sz;
     21     void clear(){sz=0;memset(head,-1,sizeof(head));}
     22     void push(int st,int ans,int cc){
     23         int h = st%HASH;
     24         for(int i=head[h];i!=-1;i=nxt[i]){
     25             if(st==state[i] && cc==cnt[i]){
     26                 f[i]=(f[i]+ans)%MOD;
     27                 return ;
     28             }
     29         }
     30         state[sz]=st,nxt[sz]=head[h],f[sz]=ans;cnt[sz]=cc;
     31         head[h]=sz++;
     32     }
     33 }hm[2];
     34 void decode(int st){
     35     for(int i=m;i>=0;--i)code[i]=st&3,st>>=2;
     36 }
     37 int encode(){
     38     int ret=0;
     39     for(int i=0;i<=m;++i) ret = ret<<2|code[i];
     40     return ret;
     41 }
     42 void shift(){
     43     for(int i=m;i;--i)code[i]=code[i-1];
     44     code[0]=0;
     45 }
     46 void dpblock(int i,int j,int cur){
     47     int mv = j==m?2:0;
     48     for(int k=0;k<hm[cur].sz;++k)
     49         hm[cur^1].push(hm[cur].state[k]>>mv, hm[cur].f[k],hm[cur].cnt[k]);
     50 }
     51 void dpblank(int i,int j,int cur){
     52     for(int k=0;k<hm[cur].sz;++k){
     53         decode(hm[cur].state[k]);
     54         int left = code[j-1], up = code[j];
     55         if(left && up){
     56             if(left==2&&up==1){
     57                 if(hm[cur].cnt[k]==K)continue;
     58                 int tmp=0;
     59                 for(int jj=j-2;jj>=0;--jj)if(code[jj])++tmp;
     60                 if(tmp&1)continue;
     61                 for(int jj=j+1;jj<=m;++jj)if(code[jj])++tmp;
     62                 if(tmp&1)continue;
     63                 code[j-1]=code[j]=0;
     64                 if(j==m)shift();
     65                 hm[cur^1].push(encode(), hm[cur].f[k], hm[cur].cnt[k]+1);
     66                 continue;
     67             }else if(left==2&&up==2){
     68                 code[j-1]=code[j]=0;
     69                 for(int jj=j+1,tmp=1;jj<=m;++jj){
     70                     if(code[jj]==2)++tmp;
     71                     if(code[jj]==1)--tmp;
     72                     if(tmp==0){code[jj]=3-code[jj];break;}
     73                 }
     74             }else if(left==1&&up==1){
     75                 code[j-1]=code[j]=0;
     76                 for(int jj=j-2,tmp=1;jj>=0;--jj){
     77                     if(code[jj]==1)++tmp;
     78                     if(code[jj]==2)--tmp;
     79                     if(tmp==0){code[jj]=3-code[jj];break;}
     80                 }
     81             }else {
     82                 code[j-1]=code[j]=0;
     83             }
     84             if(j==m)shift();
     85             hm[cur^1].push(encode(), hm[cur].f[k], hm[cur].cnt[k]);
     86         }else if(left || up){
     87             int t = left | up;
     88             if(i+1<=n && maze[i+1][j]=='.'){
     89                 code[j-1]=t,code[j]=0;
     90                 if(j==m)shift();
     91                 hm[cur^1].push(encode(), hm[cur].f[k], hm[cur].cnt[k]);
     92             }
     93             if(j+1<=m && maze[i][j+1]=='.'){
     94                 code[j-1]=0,code[j]=t;
     95                 hm[cur^1].push(encode(), hm[cur].f[k], hm[cur].cnt[k]);
     96             }
     97         }else {
     98             if(i+1<=n && j+1<=m && maze[i+1][j]=='.' && maze[i][j+1]=='.'){
     99                 code[j-1]=2,code[j]=1;
    100                 hm[cur^1].push(encode(), hm[cur].f[k], hm[cur].cnt[k]);
    101             }
    102         }
    103     }
    104 }
    105 int solve(){
    106     int cur=0;
    107     hm[0].clear();
    108     hm[0].push(0,1,0);
    109     for(int i=1;i<=n;++i){
    110         for(int j=1;j<=m;++j){
    111             hm[cur^1].clear();
    112             if(maze[i][j]=='.')dpblank(i,j,cur);
    113             else dpblock(i,j,cur);
    114             cur^=1;
    115         }
    116     }
    117     int ans=0;
    118     for(int k=0;k<hm[cur].sz;++k) if(hm[cur].cnt[k]==K) ans=(ans+hm[cur].f[k])%MOD;
    119     return ans;
    120 }
    121 int main(){
    122     int t;
    123     scanf("%d",&t);
    124     while(t--){
    125         scanf("%d%d%d",&n,&m,&K);
    126         for(int i=1;i<=n;++i)scanf("%s",maze[i]+1);
    127         printf("%d
    ",solve());
    128     }
    129     return 0;
    130 }
    HDU 4285

    至此,插头DP的入门算是完成了。。。。

  • 相关阅读:
    问题事件名称 clr20r3
    DataTable.Compute()用法
    XmlDocument.LoadXml(string) 转换问题
    反射原理读取对象属性并设置属性值
    读取XML某一个节点 条件筛选
    linq 对象属性排序
    winform与webbrowse之间的交互
    Linux(CentOS)下安装NVIDIA GPU驱动
    基于英伟达GPU训练TensorFlow模型
    基于Hadoop伪分布式模式集群搭建Spark环境
  • 原文地址:https://www.cnblogs.com/nextbin/p/3873783.html
Copyright © 2020-2023  润新知