【插头DP】BZOJ1814-Formula
【题目大意】
给出一个m*n的矩阵里面有一些格子为障碍物,求经过所有非障碍格子的哈密顿回路个数。
【思路】
最典型的插头DP。分为三种情况:
(1)当前格子既没有上插头也没有左插头。
如果下边和右边都没有障碍,新建连同分量。
(2)如果只有左插头或者右插头。
延伸或者拐弯,当然也要判断有没有障碍。
(3)上插头和左插头都没有。
1. 如果两个插头不连通(编号不一样),那么将两个插头所处的连通分量合并,标记相同的连通块标号,O(n)扫描保证最小表示;
2. 如果已经连通,相当于出现了一个回路,这种情况只能出现在最后一个非障碍格子。
由于状态非常多,用hash表存储状态。
decode和encode注意一下,这里不赘述了。
【错误点】
注意一下ch要开得够大,具体见代码。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<vector> 6 using namespace std; 7 typedef long long ll; 8 const int MAXN=14; 9 const int HASH=30007; 10 int ex,ey; 11 int m,n; 12 int maze[MAXN][MAXN]; 13 int code[MAXN],ch[MAXN]; 14 struct HashMap 15 { 16 vector<int> hash[HASH];//存储f和state的下标 17 vector<ll> f,state;//存储对应的方案数和状态 18 void init() 19 { 20 for (int i=0;i<HASH;i++) vector<int>().swap(hash[i]); 21 vector<ll>().swap(f); 22 vector<ll>().swap(state); 23 } 24 void push(ll st,ll ans) 25 { 26 int h=st%HASH; 27 for (int i=0;i<hash[h].size();i++) 28 { 29 int now=hash[h][i]; 30 if (state[now]==st)//如果已经存储了当前状态,直接累加 31 { 32 f[now]+=ans; 33 return; 34 } 35 } 36 //如果没有存储过当前状态,累加 37 state.push_back(st); 38 f.push_back(ans); 39 hash[h].push_back(state.size()-1); 40 } 41 }dp[2]; 42 43 void decode(ll st) 44 { 45 memset(code,0,sizeof(code)); 46 for (int i=n;i>=0;i--) 47 { 48 code[i]=st&7;//每三位代表一个信息 49 st>>=3; 50 } 51 } 52 53 ll encode() 54 //用最小表示法重新编码 55 { 56 int cnt=1; 57 memset(ch,-1,sizeof(ch)); 58 ch[0]=0; 59 long long st=0; 60 for (int i=0;i<=n;i++) 61 { 62 if (ch[code[i]]==-1) ch[code[i]]=cnt++; 63 code[i]=ch[code[i]]; 64 st<<=3; 65 st|=code[i]; 66 } 67 return st; 68 } 69 70 void shift() 71 { 72 for (int i=n;i>0;i--) code[i]=code[i-1]; 73 code[0]=0; 74 } 75 76 77 void dpblank(int i,int j,int cur) 78 { 79 for (int k=0;k<dp[1-cur].state.size();k++) 80 { 81 decode(dp[1-cur].state[k]); 82 int left=code[j-1];//左插头 83 int up=code[j];//上插头 84 85 /*如果上下插头都没有*/ 86 if (!left && !up) 87 { 88 if (maze[i][j+1] && maze[i+1][j]) 89 { 90 code[j-1]=code[j]=MAXN-1; 91 //这里只要随便设置一个大数即可 92 93 //【attention】这里千万不可以设置成MAXN,否则ch数组会抱★★★★★★★★ 94 95 //因为encode会重新用最小表示法编码 96 dp[cur].push(encode(),dp[1-cur].f[k]); 97 } 98 } 99 100 /*只有上插头或者只有左插头*/ 101 if ((left&&(!up))||((!left)&&up)) 102 { 103 104 int t=left|up; 105 if (maze[i][j+1])//右边没有障碍 106 { 107 code[j-1]=0; 108 code[j]=t; 109 dp[cur].push(encode(),dp[1-cur].f[k]); 110 } 111 if (maze[i+1][j])//下面没有障碍 112 { 113 code[j-1]=t; 114 code[j]=0; 115 if (j==n) shift(); 116 dp[cur].push(encode(),dp[1-cur].f[k]); 117 } 118 } 119 120 /*上插头和右插头都有*/ 121 if (left && up) 122 { 123 if (left==up) 124 { 125 if (i==ex && j==ey) 126 { 127 code[j-1]=code[j]=0; 128 if (j==n) shift(); 129 dp[cur].push(encode(),dp[1-cur].f[k]); 130 } 131 } 132 else 133 { 134 code[j-1]=code[j]=0; 135 for (int t=0;t<=n;t++) 136 if (code[t]==up) code[t]=left; 137 if (j==n) shift(); 138 dp[cur].push(encode(),dp[1-cur].f[k]); 139 } 140 } 141 } 142 } 143 144 void dpblock(int i,int j,int cur) 145 { 146 int k=0; 147 for (int k=0;k<dp[1-cur].state.size();k++) 148 { 149 decode(dp[1-cur].state[k]); 150 code[j-1]=code[j]=0; 151 if (j==n) shift(); 152 dp[cur].push(encode(),dp[1-cur].f[k]); 153 } 154 } 155 156 void solve() 157 { 158 int cur=0; 159 ll ans=0; 160 dp[cur].init(); 161 dp[cur].push(0,1);//DP数组初始化 162 for (int i=1;i<=m;i++) 163 for (int j=1;j<=n;j++) 164 { 165 cur^=1; 166 dp[cur].init(); 167 if (maze[i][j]) dpblank(i,j,cur); 168 else dpblock(i,j,cur);172 } 173 for (int i=0;i<dp[cur].state.size();i++) 174 ans+=dp[cur].f[i]; 175 printf("%lld",ans); 176 } 177 178 void init() 179 { 180 memset(maze,0,sizeof(maze)); 181 ex=ey=0; 182 for (int i=1;i<=m;i++) 183 { 184 char str[MAXN]; 185 scanf("%s",str); 186 for (int j=0;j<n;j++) 187 { 188 if (str[j]=='.') 189 { 190 ex=i; 191 ey=j+1; 192 maze[i][j+1]=1; 193 } 194 } 195 } 196 } 197 198 int main() 199 { 200 while (scanf("%d%d",&m,&n)!=EOF) 201 { 202 init(); 203 if (ex==0) puts("0");//如果没有一个是空格的话直接输出0 204 else solve(); 205 } 206 return 0; 207 }