题意:给一个n*m的矩阵,其中#是障碍格子,其他则是必走的格子,问从左下角的格子走到右下角的格子有多少种方式。

思路:

  注意有可能答案是0,就是障碍格子阻挡住了去路。

  插头DP有两种比较常见的表示连通信息的方式:

  (1)最小表示法

  (2)括号表示法

  本文用括号表示法实现。左括号为1,右括号为2,用两个位来表示。轮廓线上最多需要表示9个插头信息,那么就是18个位即可。插头的状态转移有如下几种:

  (1)右插头和下插头都是同个方向的括号,则合并他们,再将对应的两外两个半括号给改成一对。比如 ((())) 合并完变成##()(),橙色的就是需要改的地方。

  (2)右插头是')',下插头是'(',则合并他们,且无需任何修改。

  (3)右插头是'(',下插头是')',则不能合并,因为一旦合并,肯定是组成1个圆了,就会有多个连通分量的产生。自己画画就知道了。

  (4)右插头是'(',下插头是')',只有在最后一个非障碍格子(按行从左到右遍历的)的时候才可以合并,

  (5)右/下插头只有1个插头存在,那么可以延续它,可以分别往下和右两个方向。

  (6)没有插头,那么只能另开一对新括号了,分别对应右和下插头的位置。

  (7)障碍格子,只有该位置的两个插头都是空的时候才可以转移,且轮廓线无需修改。

  因为状态本来就不多,用哈希表来存状态会比较快且比较省时间,哈希表实现是摘别人的。每次只需要用上一个格子中的状态来转移到当前格子的状态。本题是不能有连通分量产生的,所以只需要在初始的状态设置起点和终点是一对括号,那就相当于在找哈密顿回路了,和Formula 1就一样了。

 //#include <bits/stdc++.h>
#include <iostream>
#include <map>
#include <vector>
#include <cstdio>
#include <cstring>
#define pii pair<int,int>
#define INF 0x3f3f3f3f
#define LL long long
using namespace std;
const int N=;
char g[N][N];
int cur, n, m, ex, ey;
struct Hash_Map
{
static const int mod=;
static const int N=;
int head[mod]; //桶指针
int next[N]; //链
int status[N]; //状态
LL value[N]; //状态对应的DP值。
int size;
void clear()
{
memset(head, -, sizeof(head));
size = ;
} void insert(int st, LL val) //插入状态st的值为val
{
int h = st%mod;
for(int i=head[h]; i!=-; i=next[i])
{
if(status[i] == st) //这个状态已经存在,累加进去。
{
value[i] += val;
return ;
}
}
status[size]= st; //新的
value[size] = val;
next[size] = head[h] ; //新插入的元素在队头
head[h] = size++;
}
}hashmap[]; int getbit(int s,int pos) //取出状态s的第pos个插头
{
int bit=;
if(s&(<<*pos)) bit+=;
if(s&(<<*pos+)) bit+=;
return bit;
}
int setbit(int s,int pos,int bit) //将状态s的第pos个插头设置为bit
{
if(s&(<<*pos )) s^=<<*pos;
if(s&(<<*pos+)) s^=<<*pos+;
return s|(bit<<*pos);
} int Fr(int s,int pos,int bit) //寻找状态s的第pos个插头对应的右括号。
{
int cnt=;
for(pos+=; pos<m; pos++)
{
if(getbit(s,pos)==-bit) cnt++;
if(getbit(s,pos)==bit) cnt--;
if(cnt==-) return setbit(s, pos, -bit);
} }
int Fl(int s,int pos,int bit) //寻找状态s的第pos个插头对应的左括号。
{
int cnt=;
for(pos--; pos>=; pos--)
{
if(getbit(s,pos)==-bit) cnt++;
if(getbit(s,pos)==bit) cnt--;
if( cnt==-) return setbit(s, pos, -bit);
} } void DP(int i,int j) //状态转移
{
for(int k=; k<hashmap[cur^].size; k++)
{
int s=hashmap[cur^].status[k];
int v=hashmap[cur^].value[k];
int R=getbit(s,j), D=getbit(s,j+);
if(g[i][j]=='.')
{
if(R && D) //两个括号
{
int t=setbit(s,j,)&setbit(s,j+,);
if(R==D) //同个方向的括号
{
if(R==) t=Fr(t,j,); //要改
else t=Fl(t,j,);
hashmap[cur].insert(t,v);
}
else if( R== && D== ) //不同方向括号
hashmap[cur].insert(t,v);
else if(i==ex&&j==ey)
hashmap[cur].insert(t,v);
}
else if(R || D) //仅1个括号
{
hashmap[cur].insert(s,v);
int t;
if(R) t=setbit(setbit(s,j,),j+,R);
else t=setbit(setbit(s,j+,),j,D);
hashmap[cur].insert(t,v);
}
else //无括号
hashmap[cur].insert( setbit(s,j,)|setbit(s,j+,), v);
}
else if(R==&&D==) //障碍格子
hashmap[cur].insert(s, v);
}
}
void cal()
{
for(int i=; i<n; i++)
{
cur^=;
hashmap[cur].clear();
for(int j=; j<hashmap[cur^].size; j++) //新行,需要左移一下状态。
if( getbit( hashmap[cur^].status[j], m)== )
hashmap[cur].insert( hashmap[cur^].status[j]<<, hashmap[cur^].value[j] );
for(int j=; j<m; j++)
{
cur^=;
hashmap[cur].clear();
DP(i,j);
if(i==ex && j==ey) return ; //终点
}
}
} bool print()
{
for(int i=; i<hashmap[cur].size; i++) //寻找轮廓线状态为0的值。
{
int s=hashmap[cur].status[i];
if(s==)
{
printf("%lld\n", hashmap[cur].value[i]);
return true;
}
}
return false;
}
int main()
{
freopen("input.txt", "r", stdin);
while(scanf("%d%d",&n,&m), n+m)
{
ex=ey=cur=;
for(int i=n-; i>=; i--) scanf("%s",g[i]); //反向存
for(int i=; i<n; i++) //寻找终点格子:ex和ey
for(int j=; j<m; j++)
if( g[i][j]=='.' )
ex=i,ey=j; hashmap[cur].clear();
hashmap[cur].insert(setbit(,,)|setbit(,m-,), ); //初始状态
cal();
if(!print()) puts(""); //无路可达
}
return ;
}

AC代码

附上哈希表实现:

 struct Hash_Map
{
static const int mod=;
static const int N=;
int head[mod]; //桶指针
int next[N]; //记录链的信息
int status[N]; //状态
LL value[N]; //状态对应的DP值。
int size;
void clear() //清除哈希表中的状态
{
memset(head, -, sizeof(head));
size = ;
} void insert(int st, LL val) //插入状态st的值为val
{
int h = st%mod;
for(int i=head[h]; i!=-; i=next[i])
{
if(status[i] == st) //这个状态已经存在,累加进去。
{
value[i] += val;
return ;
}
}
status[size]= st; //找不到状态st,则插入st。
value[size] = val;
next[size] = head[h] ; //新插入的元素在队头
head[h] = size++;
}
}hashmap[];

Hash_Map

POJ 1739 Tony's Tour (插头DP,轮廓线DP)的更多相关文章

  1. [POJ 1739] Tony's Tour

    Link: POJ 1739 传送门 Solution: 这题除了一开始的预处理,基本上就是插头$dp$的模板题了 由于插头$dp$求的是$Hamilton$回路,而此题有起点和终点的限制 于是可以构 ...

  2. POJ 1739 Tony's Tour(插头DP)

    Description A square township has been divided up into n*m(n rows and m columns) square plots (1< ...

  3. POJ 1739 Tony's Tour (DP)

    题意:从左下角到右下角有多少种走法. 析:特殊处理左下角和右下角即可. 代码如下: #pragma comment(linker, "/STACK:1024000000,1024000000 ...

  4. 【POJ】1739 Tony's Tour

    http://poj.org/problem?id=1739 题意:n×m的棋盘,'#'是障碍,'.'是空白,求左下角走到右下角且走过所有空白格子的方案数.(n,m<=8) #include & ...

  5. 【POJ】【1739】Tony's Tour

    插头DP 楼教主男人八题之一! 要求从左下角走到右下角的哈密顿路径数量. 啊嘞,我只会求哈密顿回路啊……这可怎么搞…… 容易想到:要是把起点和重点直接连上就变成一条回路了……那么我们就连一下~ 我们可 ...

  6. 轮廓线DP:poj 2279 Mr. Young's Picture Permutations

    poj 2279 Mr. Young's Picture Permutations \(solution:\) 首先摘取一些关键词:(每行不超过它后面的行)(每排学生安排高度从左到右减少)(学生的高度 ...

  7. Luogu P3886 [JLOI2009]神秘的生物 最小表示法,轮廓线DP,插头DP,动态规划

    亲手写掉的第一道最小表示法!哈哈哈太开心啦~ 不同于以往的几个插头\(dp\),这个题目的轮廓线是周围的一圈\(n\)个格子.而其所谓"插头"也变成了相邻格子的所属连通分量编号,并 ...

  8. POJ 3254 Corn Fields (状压DP,轮廓线DP)

    题意: 有一个n*m的矩阵(0<n,m<=12),有部分的格子可种草,有部分不可种,问有多少种不同的种草方案(完全不种也可以算1种,对答案取模后输出)? 思路: 明显的状压DP啦,只是怎样 ...

  9. poj 2411 Mondriaan's Dream (轮廓线DP)

    题意:有一个n*m的棋盘,要求用1*2的骨牌来覆盖满它,有多少种方案?(n<12,m<12) 思路: 由于n和m都比较小,可以用轮廓线,就是维护最后边所需要的几个状态,然后进行DP.这里需 ...

随机推荐

  1. mongodb和mysql语法对比

    MySQL: SELECT * FROM user Mongo: db.user.find() —————————————— MySQl: SELECT * FROM user WHERE name ...

  2. eclipse编译Jmeter源码

    1.在apache官网下载源码和安装包   http://jmeter.apache.org/ 2.  解压     解压安装包和源码包,     将安装包apache-jmeter-3.3 里lib ...

  3. swift日期操作

    简介:本文将介绍一些关于swift中对于日期的格式化与获取,支持swift4.0 extension Date { //格式化日期 func getDateString() -> String{ ...

  4. lightoj1063【求割点】

    题意不懂啊...... 只知道求割点. #include <bits/stdc++.h> using namespace std; typedef long long LL; typede ...

  5. android摄像头获取图像——第三弹

    相机获取图像的格式问题 android中承认的格式的参考网址为 :http://developer.android.com/reference/android/graphics/ImageFormat ...

  6. [Xcode 实际操作]一、博主领进门-(3)使用资源文件夹(Assets.xcassets)导入并管理图片素材

    目录:[Swift]Xcode实际操作 本文将演示如何使用资源文件夹(Assets.xcassets)导入并管理图片素材. [Assets.xcassets]资源文件夹可以方便的进行图片的管理, 在读 ...

  7. 2018全球十大测试工具Top2 Katalon

    引言 由Capgemini,Sogeti和Micro Focus发布的2017-2018年世界质量报告中,Katalon超越老牌测试工具UFT(源自QTP)成为黑马新秀,在全球十大自动化测试工具中排名 ...

  8. hard(2018.10.18)

    题意:给你一棵\(n\)个节点的树,\(q\)个询问,每次询问读入\(u,v,k,op\),需要满足树上有\(k\)对点的简单路径交都等于\(u,v\)之间的简单路径,\(op=1\)表示\(k\)对 ...

  9. 关于maven项目导入后缺jar包问题的处理方法

    1.maven项目的好处:方便,我们在把自己写好的代码发送给目标人物时可以精简代码——把target目录,一些设置目录之类的都删除掉,接下来要做的就是尽可能的只留下一个src文件夹及pom.xml文件 ...

  10. 【js】我们需要无限滚动列表吗?

    无限滚动列表,顾名思义,是能够无限滚动的列表(愿意是指那些能够不断缓冲加载新数据的列表的).但是,我们真的需要这样一个列表吗?在PC端,浏览器的性能其实已经能够满足海量dom节点的渲染刷新(笔者经过简 ...