【插头DP】BZOJ3125-city

开学忙成狗,刷题慢如蜗牛……

【题目大意】

给出一个m*n的矩阵里面有一些格子为障碍物,一些格子只能上下通行,一些格子只能左右通行,一些格子上下左右都能通行。问经过所有非障碍格子的哈密顿回路个数。

【思路】

和BZOJ1814差不多,增加几个比较简单的判断即可。详见代码。

【错误点】

一开始我把三种可通行的情况写在一起,有点混乱了……拆开成3个函数就好啦!

下次注意程序的可读性。

  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 DpHorizon(int cur,int i,int j)//只能水平走 
145 {
146     for (int k=0;k<dp[1-cur].state.size();k++)
147     {
148         decode(dp[1-cur].state[k]);
149         int left=code[j-1],up=code[j];
150         
151         if(!up && left&& maze[i][j+1]!=0 && maze[i][j+1]!=3&& j!=n&&j!=1) //右边不是障碍物,也能够水平走,并且不是最右边 
152         {
153             code[j-1]=0;
154             code[j]=left;
155             dp[cur].push(encode(),dp[1-cur].f[k]);
156         }     
157     } 
158     
159 }
160 
161 void DpVertical(int cur,int i,int j)//只能竖直走 
162 {
163     for (int k=0;k<dp[1-cur].state.size();k++)
164     {
165         decode(dp[1-cur].state[k]);
166         int left=code[j-1],up=code[j];
167         
168         if(up && !left && maze[i+1][j]!=0 && maze[i+1][j]!=2&&i!=m&&i!=1)//下边不是障碍物,也能够竖直走 
169         {
170             code[j-1]=up;
171             code[j]=0;
172             if(j==n)shift();
173             dp[cur].push(encode(),dp[1-cur].f[k]);
174         }     
175     } 
176     
177 }
178 
179 void dpblock(int i,int j,int cur)
180 {
181     int k=0;
182     for (int k=0;k<dp[1-cur].state.size();k++)
183     {
184         decode(dp[1-cur].state[k]);
185         code[j-1]=code[j]=0;
186         if (j==n) shift();
187         dp[cur].push(encode(),dp[1-cur].f[k]);
188     }
189 }
190 
191 void solve()
192 {
193     int cur=0;
194     ll ans=0;
195     dp[cur].init();
196     dp[cur].push(0,1);//DP数组初始化 
197     for (int i=1;i<=m;i++)
198         for (int j=1;j<=n;j++)
199         {
200             cur^=1;
201             dp[cur].init();
202             if (maze[i][j]==1) dpblank(i,j,cur);
203                 else if (maze[i][j]==0)  dpblock(i,j,cur);
204                 else if  (maze[i][j]==2)DpHorizon(cur,i,j);
205                 else  DpVertical(cur,i,j);
206         }
207     for (int i=0;i<dp[cur].state.size();i++)
208         ans+=dp[cur].f[i];
209     printf("%lld",ans);
210 }
211 
212 
213 void init()
214 {
215     scanf("%d%d",&m,&n);
216     for (int i=1;i<=m;i++)
217     {
218         char str[MAXN];
219         scanf("%s",str);
220         for (int j=1;j<=n;j++)
221         {
222             if (str[j-1]=='#') maze[i][j]=0;
223             if (str[j-1]=='.') maze[i][j]=1;
224             if (str[j-1]=='-') maze[i][j]=2;
225             if (str[j-1]=='|') maze[i][j]=3;
226             if (str[j-1]!='#') ex=i,ey=j;
227         }
228     }
229 }
230 
231 int main()
232 {
233 
234     init();
235     if (ex==0) puts("0");//如果没有一个是空格的话直接输出0 
236         else solve();
237    
238 }

 

posted @ 2016-09-13 22:25  iiyiyi  阅读(368)  评论(0编辑  收藏  举报