FZU 1977 Pandora adventure (插头DP,常规)

题意:有一个n*m矩阵,其中有些格子必走,有些格子不可走,其他格子是可走也可不走,问有多少条哈密顿回路?

思路:

  本来是一道很简单的题,代码写多了连白痴bug都查不出了,竟然用i>=ex&&j>=ey来判定最后一个必走点后面的点!明显是错的。

  其实主要在选走的格子,那么有两种选择,“走”or“不走”,不走的话上一个格子的状态照搬过来。这样就没有了固定的终点了,因为终点可以是很多种情况(比如是选走点/必走点),那么只要是在ex和ey后面的格子(指的是(ex,ey)之后遍历的所有非障碍格子),都是可以作为终点的,只有在这些格子才可以将左右括号连起来(只统计但不插入)。其他的基本和常规的一样了,本次仍然用括号表示法,再过滤掉很多无效的状态,所以代码略长,但容易看。

  1 //#include <bits/stdc++.h>
  2 #include <iostream>
  3 #include <cstdio>
  4 #include <cstring>
  5 #define pii pair<int,int>
  6 #define INF 0x3f3f3f3f
  7 #define LL long long
  8 using namespace std;
  9 const int N=20;
 10 const int mod=12357;
 11 const int NN=100010;
 12 char g[N][N];
 13 LL ans;
 14 int cur, n, m, ex, ey, tx, ty;
 15 struct Hash_Map
 16 {
 17     int head[mod];      //桶指针
 18     int next[NN];        //记录链的信息
 19     LL  status[NN];      //状态
 20     LL  value[NN];       //状态对应的DP值。
 21     int size;
 22 
 23     void clear()    //清除哈希表中的状态
 24     {
 25         memset(head, -1, sizeof(head));
 26         size = 0;
 27     }
 28 
 29     void insert(LL st, LL val)  //插入状态st的值为val
 30     {
 31         int h = st%mod;
 32         for(int i=head[h]; ~i; i=next[i])
 33             if(status[i] == st) //这个状态已经存在,累加进去。
 34             {
 35                 value[i] += val;
 36                 return ;
 37             }
 38         status[size]= st;           //找不到状态st,则插入st。
 39         value[size] = val;
 40         next[size] = head[h] ;      //新插入的元素在队头
 41         head[h] = size++;
 42     }
 43 }hashmap[2];
 44 
 45 inline int getbit(LL s,int pos)   //取出状态s的第pos个插头
 46 {
 47     return (s>>2*pos)&3;
 48 }
 49 inline int setbit(LL s,int pos,int bit)   //将状态s的第pos个插头设置为bit
 50 {
 51     if(s&(1<<2*pos ))     s^=1<<(2*pos);
 52     if(s&(1<<(2*pos+1)))  s^=1<<(2*pos+1);
 53     return (s|(bit<<2*pos));
 54 }
 55 
 56 int Fr(LL s,int pos,int bit)   //寻找状态s的第pos个插头对应的右括号。
 57 {
 58     int cnt=0;
 59     for(pos+=2; pos<m; pos++)
 60     {
 61         if(getbit(s, pos)==3-bit)   cnt++;
 62         if(getbit(s, pos)==bit)     cnt--;
 63         if(cnt==-1)         return setbit(s, pos, 3-bit);
 64     }
 65 }
 66 int Fl(LL s,int pos,int bit)   //寻找状态s的第pos个插头对应的左括号。
 67 {
 68     int cnt=0;
 69     for(pos--; pos>=0; pos--)
 70     {
 71         if(getbit(s, pos)==3-bit)  cnt++;
 72         if(getbit(s, pos)==bit)    cnt--;
 73         if( cnt==-1)    return setbit(s, pos, 3-bit);
 74     }
 75 }
 76 
 77 void DP(int i,int j)    //非障碍空格
 78 {
 79     LL t;
 80     for(int k=0; k<hashmap[cur^1].size; k++)
 81     {
 82         LL s=hashmap[cur^1].status[k];
 83         LL v=hashmap[cur^1].value[k];
 84         int R=getbit(s,j), D=getbit(s,j+1);
 85         if(g[i][j]=='X')    //障碍格子
 86         {
 87             if( R==0 && D==0 )    hashmap[cur].insert(s, v);
 88             continue ;
 89         }
 90         if(R && D)  //两个括号
 91         {
 92             t=(setbit(s,j,0)&setbit(s,j+1,0));
 93             if(R==D)    //同个方向的括号
 94             {
 95                 if(R==1)    t=Fr(t, j, 2);  //要改
 96                 else        t=Fl(t, j, 1);
 97                 hashmap[cur].insert(t, v);
 98             }
 99             else if( R==2 && D==1 )        //不同的连通分量
100                 hashmap[cur].insert(t, v);
101             else if(t==0 && ( i>ex || (i==ex && j>=ey)))    //注意点!
102                 ans+=v;
103         }
104         else if(R || D)     //仅1个括号
105         {
106             if(R)
107             {
108                 if(i+1<n && g[i+1][j]!='X')   hashmap[cur].insert(s, v);
109                 if(j+1<m && g[i][j+1]!='x')   hashmap[cur].insert(setbit(setbit(s,j,0), j+1, R), v);
110             }
111             else
112             {
113                 if(j+1<m && g[i][j+1]!='X')   hashmap[cur].insert(s, v);
114                 if(i+1<n && g[i+1][j]!='X')   hashmap[cur].insert(setbit(setbit(s,j+1,0), j, D), v);
115             }
116         }
117         else                //无括号
118         {
119             if( g[i][j+1]!='X' && g[i+1][j]!='X' && j+1<m && i+1<n )    //新括号
120                 hashmap[cur].insert( setbit(s,j,1)|setbit(s,j+1,2), v);
121             if( g[i][j]=='*' )      //此点可不走
122                 hashmap[cur].insert( s, v);
123         }
124     }
125 }
126 
127 void cal()
128 {
129     for(int i=0; i<n; i++)
130     {
131         cur^=1;
132         hashmap[cur].clear();
133         for(int j=0; j<hashmap[cur^1].size; j++)    //新行,需要左移一下状态。
134             hashmap[cur].insert( hashmap[cur^1].status[j]<<2, hashmap[cur^1].value[j] );
135         for(int j=0; j<m; j++)
136         {
137             cur^=1;
138             hashmap[cur].clear();
139             DP(i,j);
140             if(i==tx && j==ty)    return ;  //最后的有效点
141         }
142     }
143 }
144 
145 
146 int main()
147 {
148     //freopen("input.txt", "r", stdin);
149     int t, Case=0;
150     cin>>t;
151     while(t--)
152     {
153         tx=ty=ex=ey=-1;
154         ans=cur=0;
155         memset(g, 'X', sizeof(g));
156         scanf("%d%d",&n,&m);
157         for(int i=0; i<n; i++)      //输入矩阵
158         {
159             for(int j=0; j<m; j++)
160             {
161                 char c=getchar();
162                 if(c=='X'||c=='*'||c=='O')
163                 {
164                     g[i][j]=c;
165                     if( c=='O' )    ex=i,ey=j;  //必走格
166                     if( c!='X' )    tx=i,ty=j;  //终点格
167                 }
168                 else    j--;
169             }
170         }
171 
172         hashmap[cur].clear();
173         hashmap[cur].insert(0, 1);  //初始状态
174         cal();
175         cout<<"Case "<<++Case<<": "<<ans<<endl;
176     }
177     return 0;
178 }
AC代码
原文地址:https://www.cnblogs.com/xcw0754/p/4789842.html