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