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

相关文章:

  • 2021-08-13
  • 2021-10-03
  • 2022-12-23
  • 2021-08-26
  • 2022-12-23
猜你喜欢
  • 2022-12-23
  • 2021-06-17
  • 2021-04-03
  • 2021-11-25
相关资源
相似解决方案