标签: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也可。
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 }
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\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 }
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\n",++ca,ox==n+1?0:solve()); 130 } 131 return 0; 132 }
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();// ‘\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 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\n",++ca,solve()); 117 } 118 return 0; 119 }
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\n",ans); 133 continue; 134 } 135 printf("%I64d\n",solve()); 136 } 137 return 0; 138 }
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\n",ans==inf?0:ans+2); 127 } 128 return 0; 129 }
============================接下来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\n",solve()); 133 } 134 return 0; 135 }
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\n",ans.a[1][0]); 130 } 131 return 0; 132 }
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\n",solve()); 139 } 140 return 0; 141 }
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\n",solve()); 128 } 129 return 0; 130 }
至此,插头DP的入门算是完成了。。。。
标签:style blog http color 使用 os io 数据
原文地址:http://www.cnblogs.com/nextbin/p/3873783.html