【插头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 } 

 

posted @ 2016-09-06 18:49  iiyiyi  阅读(817)  评论(0编辑  收藏  举报