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代码

时间: 2024-08-07 08:40:00

FZU 1977 Pandora adventure (插头DP,常规)的相关文章

【插头DP】 FZU 1977 Pandora adventure

通道:http://acm.fzu.edu.cn/problem.php?pid=1977 题意:单回路,有障碍点,必走点和非必走点. 思路:由于有格子可以不经过,那么就导致最后一个格子无法确定,那么额外增加一个标志位来记录是否形成回路,如果形成了回路,而后面又遇到了插头或者必须要经过的点,那么就剪掉

【FZU】1977 Pandora adventure

http://acm.fzu.edu.cn/problem.php?pid=1977 题意:n×m的网格,有3种格子,'O'必须经过.'*'可以选择经过.'X'不能经过.现在要求路径经过所有'O'且是简单回路的数量 #include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef long long ll; struct H { static const int

HDU 3377 Plan (插头DP,变形)

题意:有一个n*m的矩阵,每个格子中有一个值(可能负值),要从左上角走到右下角,求路径的最大花费. 思路: 除了起点和终点外,其他的点可以走,也可以不走. (2)我用的是括号表示法,所以起始状态为')',即仅有一个右括号,那么到右下角也应该是只有一个右括号.因为,如果碰到()),加粗表示起点的那个右括号,那么合并后变成)##,仍然是右括号,如果是)(),那么合并后变成##),仍然是右括号,相当于延续了.插头每到达一个格子就先将其值给加上,如果要合并的时候,再减掉(因为多算了一次),因此,新括号的

插头DP专题

建议入门的人先看cd琦的<基于连通性状态压缩的动态规划问题>.事半功倍. 插头DP其实是比较久以前听说的一个东西,当初是水了几道水题,最近打算温习一下,顺便看下能否入门之类. 插头DP建议先理解“插头”的概念.然后会HASH表(这个其实是很基础的东西,应该都会的).然后就是DP. 以及特殊题目的特殊处理. 好像一般是求N,M<=12的网格图的某种回路数或某种通路数的方案数. 大体上每个题说几句特殊处理,有问题请纠正....题目的顺序基本上难度递增 另外代码我都是用括号匹配的.因为感觉连通

fzu1977之插头DP

 Problem 1977 Pandora adventure Accept: 354    Submit: 1177 Time Limit: 1000 mSec    Memory Limit : 32768 KB  Problem Description The pollution of the earth is so serious that people can not survive any more. Fortunately, people have found a new plan

URAL 1519 Formula 1 (插头DP,常规)

题意:给一个n*m的矩阵,格子中是'*'则是障碍格子,不允许进入,其他格子都是必走的格子,所走格子形成一条哈密顿回路,问有多少种走法? 思路: 本来是很基础的题,顿时不知道进入了哪个坑.这篇插头DP的文章够详细,推荐一看(最好不要照抄代码). 细节要小心,比如输出用I64d,必须用long long,判断是否无哈密顿回路,位操作等等. 这次仍然用括号表示法,1表示(,2表示). 1 #include <bits/stdc++.h> 2 #define pii pair<int,int&g

插头dp

对于网格中的dp可以用轮廓线,如果有一些格子不能走就可以用插头dp了. bzoj2331 地板 题目大意:用L型铺地n*m,有一些格子不能铺,求方案数. 思路:fi[i][j][s]表示铺到(i,j),轮廓线状态s,0表示没有插头,1表示插头没拐弯,2表示插头拐弯了,手动枚举转移. 注意:(1)按四进制好写: (2)因为实际状态和四进制的差很多,所以用hash表来存储,防止mle和tle,同时使用滚动数组. #include<iostream> #include<cstdio> #

[入门向选讲] 插头DP:从零概念到入门 (例题:HDU1693 COGS1283 BZOJ2310 BZOJ2331)

转载请注明原文地址:http://www.cnblogs.com/LadyLex/p/7326874.html 最近搞了一下插头DP的基础知识……这真的是一种很锻炼人的题型…… 每一道题的状态都不一样,并且有不少的分类讨论,让插头DP十分锻炼思维的全面性和严谨性. 下面我们一起来学习插头DP的内容吧! 插头DP主要用来处理一系列基于连通性状态压缩的动态规划问题,处理的具体问题有很多种,并且一般数据规模较小. 由于棋盘有很特殊的结构,使得它可以与“连通性”有很强的联系,因此插头DP最常见的应用要数

插头DP学习

队内没人会插头DP,感觉这个不会不行...所以我还是默默去学了一下, 学了一天,感觉会了一点.对于每一行,一共有j+1个插头,如果是多回路类的题目, 比较简单,可以用1表示有插头,0表示没有插头,这样就可以愉快转移了, 对于当前出来的位置(i,j),与它有关的插头有j-1和j 那么我们可以枚举状态经行转移. 对于单回路的问题....只是了解思想,目前还不会写,太笨了=_= poj 2411 Mondriaan's Dream 题意:用1*2小方块组成n*m矩阵有多少种组成方式 思路:我们从上到下