插头DP基础题的样子。。。输入N,M<=11,以及N*M的01矩阵,0(1)表示有(无)障碍物。输出哈密顿回路(可以多回路)方案数。。。
看了个ppt,画了下图。。。感觉还是挺有效的。。。
参考http://www.cnblogs.com/kuangbin/archive/2012/10/02/2710343.html
以及推荐cd琦的论文ppthttp://wenku.baidu.com/view/4fe4ac659b6648d7c1c74633.html
向中学生学习~~
感觉以后可能还会要看这篇日志。所以特意加了较多的注释。。。观客可以看注释=。=推荐画示意图。。。。
复杂度O(n*m*2^(m+1))
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <algorithm> 5 #include <cmath> 6 #include <string> 7 #include <vector> 8 #include <queue> 9 #include <set> 10 using namespace std; 11 12 #define ll long long 13 #define inf 0x3f3f3f3f 14 #define eps 1e-8 15 16 int a[12][12]; 17 ll dp[12][12][1<<12]; 18 int main(){ 19 int t,ca=0; 20 scanf("%d",&t); 21 while(t--){ 22 int n,m; 23 scanf("%d%d",&n,&m); 24 for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)scanf("%d",&a[i][j]); 25 memset(dp,0,sizeof(dp)); 26 dp[1][0][0]=1; 27 int ma=(1<<(m+1)); 28 for(int i=1;i<=n;++i){ 29 for(int j=1;j<=m;++j){ 30 for(int k=0;k<ma;++k){ 31 int d=1<<(j-1),r=1<<j; 32 if(a[i][j]){// 该位置无障碍 33 if( ( (k&d)&&(k&r) ) || ( (~k&d)&&(~k&r)) )// 右插下插同时有或同时无,必须翻转 34 dp[i][j][k]=dp[i][j-1][k^d^r]; 35 else // 右插下插只有一个,可翻转可不翻转 36 dp[i][j][k]=dp[i][j-1][k]+dp[i][j-1][k^d^r]; 37 } 38 else {// 该位置有障碍 39 if((k&d)==0&&(k&r)==0)// 没有右插没有下插,则方案数跟随 40 dp[i][j][k]=dp[i][j-1][k]; 41 } 42 } 43 } 44 if(i+1<=11) 45 for(int k=0;k<(1<<m);++k)// 换行去掉上一行的右插,下一行的开头没有进来的左插,请画图 46 dp[i+1][0][k<<1]=dp[i][m][k]; 47 } 48 printf("Case %d: There are %I64d ways to eat the trees.\n",++ca,dp[n][m][0]); 49 } 50 return 0; 51 }
做了上面那个入门级别的插头DP。。。做了个还是入门级别的。。。。
输入N,M<=12,以及N*M的01矩阵,'*'('.')表示有(无)障碍物。输出哈密顿单回路方案数。。。
这一题需要用到括号匹配,最小表示法(上面那题可以不用,我的代码就是没有用到的)。。。
这一题参考了别人的代码,尝试了2种表示方法,一种是直接存括号对应的连通分量标号,一种是只存括号的类型。。。
我的第一种方法300+ms,第二种100+ms。。。感觉还是有点区别的。。。因为存连通分量标号需要用到3位(连通分量标号最大值为MAXM/2),而括号类型只需要2位
。。
而这题跟上一题的区别,就是,单哈密顿回路的括号匹配,如果左插是'(',下插是')',那只能在最后一个非障碍物的位置连接。。。。
具体看代码吧。。。还是建议画下示意图。。。
还有一点就是,由于用到HASH,所以HASH太大太小都可能会TLE
300+ms的版本(其实就是那个链接里的。。。。):
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <algorithm> 5 #include <cmath> 6 #include <string> 7 #include <vector> 8 #include <queue> 9 #include <set> 10 using namespace std; 11 12 #define ll long long 13 #define inf 0x3f3f3f3f 14 #define eps 1e-8 15 16 #define maxd 15 17 #define HASH 100007 18 #define STATE 1000010 19 20 int n,m; 21 int maze[maxd][maxd],code[maxd]; 22 int ch[maxd];//最小表示法使用 23 int ex,ey;//最后一个非障碍格子的坐标 24 struct HASHMAP{ 25 int head[HASH],nxt[STATE],sz; 26 ll state[STATE]; 27 ll f[STATE]; 28 void clear(){sz=0;memset(head,-1,sizeof(head));} 29 void push(ll st,ll ans){ 30 int h=st%HASH; 31 for(int i=head[h];i!=-1;i=nxt[i]) 32 if(state[i]==st){ 33 f[i]+=ans; 34 return; 35 } 36 state[sz]=st; 37 f[sz]=ans; 38 nxt[sz]=head[h]; 39 head[h]=sz++; 40 } 41 }hm[2]; 42 void decode(int *code,int m,ll st){ 43 for(int i=m;i>=0;i--)code[i]=st&7,st>>=3; 44 } 45 ll encode(int *code,int m){//最小表示法 46 int cnt=1; 47 memset(ch,-1,sizeof(ch)); 48 ch[0]=0; 49 ll st=0; 50 for(int i=0;i<=m;i++){ 51 if(ch[code[i]]==-1)ch[code[i]]=cnt++; 52 code[i]=ch[code[i]]; 53 st<<=3; 54 st|=code[i]; 55 } 56 return st; 57 } 58 void shift(int *code,int m){ 59 for(int i=m;i>0;i--)code[i]=code[i-1]; 60 code[0]=0; 61 } 62 void dpblank(int i,int j,int cur){ 63 for(int k=0;k<hm[cur].sz;k++){ 64 decode(code,m,hm[cur].state[k]); 65 int left=code[j-1],up=code[j]; 66 if(left&&up){ 67 if(left==up){//只能出现在最后一个非障碍格子 68 if(i==ex&&j==ey){ 69 code[j-1]=code[j]=0; 70 if(j==m)shift(code,m); 71 hm[cur^1].push(encode(code,m),hm[cur].f[k]); 72 } 73 } 74 else{//不在同一个连通分量则合并 75 code[j-1]=code[j]=0; 76 for(int t=0;t<=m;t++) 77 if(code[t]==up)code[t]=left; 78 if(j==m)shift(code,m); 79 hm[cur^1].push(encode(code,m),hm[cur].f[k]); 80 } 81 } 82 else if(left || up){ 83 int t = left + up; 84 if(maze[i][j+1]){ 85 code[j-1]=0; 86 code[j]=t; 87 hm[cur^1].push(encode(code,m),hm[cur].f[k]); 88 } 89 if(maze[i+1][j]){ 90 code[j-1]=t; 91 code[j]=0; 92 if(j==m)shift(code,m); 93 hm[cur^1].push(encode(code,m),hm[cur].f[k]); 94 } 95 } 96 else{//无插头,则构造新的连通块 97 if(maze[i][j+1]&&maze[i+1][j]){ 98 code[j-1]=code[j]=13; 99 hm[cur^1].push(encode(code,m),hm[cur].f[k]); 100 } 101 } 102 } 103 } 104 void dpblock(int i,int j,int cur){ 105 for(int k=0;k<hm[cur].sz;k++){ 106 decode(code,m,hm[cur].state[k]); 107 code[j-1]=code[j]=0; 108 if(j==m)shift(code,m); 109 hm[cur^1].push(encode(code,m),hm[cur].f[k]); 110 } 111 } 112 char str[maxd]; 113 void init(){ 114 memset(maze,0,sizeof(maze)); 115 ex=0; 116 for(int i=1;i<=n;i++){ 117 scanf("%s",str+1); 118 for(int j=1;j<=m;j++){ 119 if(str[j]=='.'){// 无障碍 120 ex=i,ey=j; 121 maze[i][j]=1; 122 } 123 } 124 } 125 } 126 void solve(){ 127 int cur=0; 128 ll ans=0; 129 hm[0].clear(); 130 hm[0].push(0,1); 131 for(int i=1;i<=n;i++) 132 for(int j=1;j<=m;j++){ 133 hm[cur^1].clear(); 134 if(maze[i][j])dpblank(i,j,cur); 135 else dpblock(i,j,cur); 136 cur^=1; 137 } 138 for(int i=0;i<hm[cur].sz;i++) 139 ans+=hm[cur].f[i]; 140 printf("%I64d\n",ans); 141 } 142 int main(){ 143 while(~scanf("%d%d",&n,&m)){ 144 init(); 145 if(ex==0)puts("0");// 没有空的格子 146 else solve(); 147 } 148 return 0; 149 }
100+ms的版本(改编自那个链接里的。。。。):
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <algorithm> 5 #include <cmath> 6 #include <string> 7 #include <vector> 8 #include <queue> 9 #include <set> 10 using namespace std; 11 12 #define ll long long 13 #define inf 0x3f3f3f3f 14 #define eps 1e-8 15 16 #define maxd 15 17 #define HASH 100007 18 #define STATE 1000010 19 20 int n,m; 21 int maze[maxd][maxd],code[maxd]; 22 int ch[maxd];//最小表示法使用 23 int ex,ey;//最后一个非障碍格子的坐标 24 struct HASHMAP{ 25 int head[HASH],nxt[STATE],sz; 26 ll state[STATE]; 27 ll f[STATE]; 28 void clear(){sz=0;memset(head,-1,sizeof(head));} 29 void push(ll st,ll ans){ 30 int h=st%HASH; 31 for(int i=head[h];i!=-1;i=nxt[i]) 32 if(state[i]==st){ 33 f[i]+=ans; 34 return; 35 } 36 state[sz]=st; 37 f[sz]=ans; 38 nxt[sz]=head[h]; 39 head[h]=sz++; 40 } 41 }hm[2]; 42 void decode(int *code,int m,ll st){ 43 for(int i=0;i<=m;++i)code[i]=st&3,st>>=2; 44 } 45 ll encode(int *code,int m){//最小表示法 46 ll st=0; 47 for(int i=m;i>=0;--i) 48 st=st<<2|code[i]; 49 return st; 50 } 51 void shift(int *code,int m){ 52 for(int i=m;i>0;i--)code[i]=code[i-1]; 53 code[0]=0; 54 } 55 void dpblank(int i,int j,int cur){ 56 for(int k=0;k<hm[cur].sz;k++){ 57 decode(code,m,hm[cur].state[k]); 58 int left = code[j-1],up = code[j]; 59 if(left&&up){ 60 if(left==2 && up==1){// 只能出现在最后一个非障碍格子 61 if(i==ex&&j==ey){ 62 code[j-1]=code[j]=0; 63 ll tmp = encode(code,m); 64 if(j==m)tmp<<=2; 65 hm[cur^1].push(tmp,hm[cur].f[k]); 66 } 67 } 68 else{//不在同一个连通分量则合并 69 if(left==1 && up==1){// ...(...(...))... ==> ...(...)...##... 70 for(int jj=j-1,cnt=0;jj>=0;--jj){ 71 if(code[jj]==1)++cnt; 72 else if(code[jj]==2)--cnt; 73 if(cnt==0){code[jj]=3-code[jj];break;} 74 } 75 code[j-1]=code[j]=0; 76 } 77 else if(left==2 && up==2){// ...((...)...)... ==> ...##...(...)... 78 for(int jj=j,cnt=0;jj<=m;++jj){ 79 if(code[jj]==1)++cnt; 80 else if(code[jj]==2)--cnt; 81 if(cnt==0){code[jj]=3-code[jj];break;} 82 } 83 code[j-1]=code[j]=0; 84 } 85 else if(left==1 && up==2){// ...(...)(...)... ==> ...(...##...)... 86 code[j-1]=code[j]=0; 87 } 88 if(j==m)shift(code,m); 89 hm[cur^1].push(encode(code,m),hm[cur].f[k]); 90 } 91 } 92 else if(left || up){ 93 int t = left | up; 94 if(maze[i][j+1]){ 95 code[j-1]=0; 96 code[j]=t; 97 hm[cur^1].push(encode(code,m),hm[cur].f[k]); 98 } 99 if(maze[i+1][j]){ 100 code[j-1]=t; 101 code[j]=0; 102 if(j==m)shift(code,m); 103 hm[cur^1].push(encode(code,m),hm[cur].f[k]); 104 } 105 } 106 else{//无插头,则构造新的连通块 107 if(maze[i][j+1]&&maze[i+1][j]){ 108 code[j-1]=2,code[j]=1; 109 hm[cur^1].push(encode(code,m),hm[cur].f[k]); 110 } 111 } 112 } 113 } 114 void dpblock(int i,int j,int cur){ 115 for(int k=0;k<hm[cur].sz;k++){ 116 decode(code,m,hm[cur].state[k]); 117 code[j-1]=code[j]=0; 118 if(j==m)shift(code,m); 119 hm[cur^1].push(encode(code,m),hm[cur].f[k]); 120 } 121 } 122 char str[maxd]; 123 void init(){ 124 memset(maze,0,sizeof(maze)); 125 ex=0; 126 for(int i=1;i<=n;i++){ 127 scanf("%s",str+1); 128 for(int j=1;j<=m;j++){ 129 if(str[j]=='.'){// 无障碍 130 ex=i,ey=j; 131 maze[i][j]=1; 132 } 133 } 134 } 135 } 136 void solve(){ 137 int cur=0; 138 ll ans=0; 139 hm[0].clear(); 140 hm[0].push(0,1); 141 for(int i=1;i<=n;i++) 142 for(int j=1;j<=m;j++){ 143 hm[cur^1].clear(); 144 if(maze[i][j])dpblank(i,j,cur); 145 else dpblock(i,j,cur); 146 cur^=1; 147 } 148 for(int i=0;i<hm[cur].sz;i++) 149 ans+=hm[cur].f[i]; 150 printf("%I64d\n",ans); 151 } 152 int main(){ 153 while(~scanf("%d%d",&n,&m)){ 154 init(); 155 if(ex==0)puts("0");// 没有空的格子 156 else solve(); 157 } 158 return 0; 159 }