码迷,mamicode.com
首页 > 其他好文 > 详细

插头DP专题

时间:2014-08-05 13:49:30      阅读:415      评论:0      收藏:0      [点我收藏+]

标签:style   blog   http   color   使用   os   io   数据   

建议入门的人先看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也可。

bubuko.com,布布扣
  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.\n",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位表示括号类型,闭合时判断是否为合法闭合处(最后一个可走的格子)。

bubuko.com,布布扣
  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\n",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位表示括号类型,将可走可不走和必须走的归为一类,可走可不走只有当没有左插且没有上插的时候,才有机会变成不走的情况,其他情况都是必须走(因为要把插头搞掉)。闭合时判断是否为合法闭合处(必须走的格子都出现完了,即当前格子序>=最后一个必须走的格子的格子序)。

bubuko.com,布布扣
  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\n",++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)。

bubuko.com,布布扣
  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();// ‘\n‘
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();// ‘\n‘
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();// ‘\n‘
137         }
138         printf("%d\n",solve());
139     }
140     return 0;
141 }
HDU 1964

HDU 3377 - Plan

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

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

bubuko.com,布布扣
  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\n",++ca,solve());
117     }
118     return 0;
119 }
HDU 3377

 POJ 1739 - Tony‘s Tour

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

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

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

bubuko.com,布布扣
  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\n",ans);
133             continue;
134         }
135         printf("%I64d\n",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"的四个位置,具体看代码的特判部分。

bubuko.com,布布扣
  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\n",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.换列平移)

bubuko.com,布布扣
  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\n",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.插头位置)

bubuko.com,布布扣
  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\n",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]的和。

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

bubuko.com,布布扣
  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\n",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

bubuko.com,布布扣
  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\n",solve());
128     }
129     return 0;
130 }
HDU 4285

 

 

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

插头DP专题,布布扣,bubuko.com

插头DP专题

标签:style   blog   http   color   使用   os   io   数据   

原文地址:http://www.cnblogs.com/nextbin/p/3873783.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!