您的位置:首页 > 其它

URAL 1519 Formula 1(插头DP)

2017-07-31 13:52 525 查看


1519. Formula 1

Time limit: 1.0 second

Memory limit: 64 MB

Background

Regardless of the fact, that Vologda could not get rights to hold the Winter Olympic games of 20**, it is well-known, that the city will conduct one of the Formula 1 events. Surely, for such an important
thing a new race circuit should be built as well as hotels, restaurants, international airport - everything for Formula 1 fans, who will flood the city soon. But when all the hotels and a half of the restaurants were built, it appeared, that at the site for
the future circuit a lot of gophers lived in their holes. Since we like animals very much, ecologists will never allow to build the race circuit over the holes. So now the mayor is sitting sadly in his office and looking at the map of the circuit with all
the holes plotted on it.

Problem

Who will be smart enough to draw a plan of the circuit and keep the city from inevitable disgrace? Of course, only true professionals - battle-hardened programmers from the first team of local technical
university!.. But our heroes were not looking for easy life and set much more difficult problem: "Certainly, our mayor will be glad, if we find how many ways of building the circuit are there!" - they said.

It should be said, that the circuit in Vologda is going to be rather simple. It will be a rectangle N*M cells in size with a single circuit segment built through each
cell. Each segment should be parallel to one of rectangle's sides, so only right-angled bends may be on the circuit. At the picture below two samples are given for N = M = 4 (gray squares mean gopher holes, and the bold black
line means the race circuit). There are no other ways to build the circuit here.



Input

The first line contains the integer numbers N and M (2 ≤ NM ≤ 12). Each of the next N lines contains M characters,
which are the corresponding cells of the rectangle. Character "." (full stop) means a cell, where a segment of the race circuit should be built, and character "*" (asterisk) - a cell, where a gopher hole is located. There are at least 4 cells without gopher
holes.

Output

You should output the desired number of ways. It is guaranteed, that it does not exceed 263-1.

Samples

inputoutput
4 4
**..
....
....
....

2

4 4
....
....
....
....

6

Problem Author: Nikita Rybak, Ilya Grebnov, Dmitry Kovalioff
Problem Source: Timus Top Coders: Third Challenge

题目大意:

    给你一个n*m的矩阵,有些格子可以走,有些不能走,问有多少条哈密顿回路。

解题思路:

    插头dp入门题,求解方法请看cdq的《基于连通性状态压缩的动态规划问题》。这里使用了最小表示法进行编码。

AC代码:

/*
*为了方便轮廓线的表示,矩阵下标从1开始
*/

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <ctime>
#include <vector>
#include <queue>
#include <stack>
#include <deque>
#include <string>
#include <map>
#include <set>
#include <list>
using namespace std;
#define INF 0x3f3f3f3f
#define LL long long
#define fi first
#define se second
#define mem(a,b) memset((a),(b),sizeof(a))

const int MAXN=12+2;
int N,M;
int ey,ex;//终点坐标
bool maze[MAXN][MAXN];//0表示不能走,1表示必须走
int tmp_state[MAXN];//保存临时轮廓线状态
int ch[MAXN];//最小表示法使用

struct HashMap
{
const static int mod=30007;//小一点
const static int maxn=1000100;//大概状态数大一些
int head[mod];//链表头指针
int next[maxn];//指向链表下一个节点
int size;//当前节点数
LL key[maxn],val[maxn];//键,值
void clear()
{
size=0;
memset(head,-1,sizeof head);
}
inline void insert(LL _key,LL _val)
{
int p=_key%mod;//取模后对应的链
for(int i=head[p];~i;i=next[i])
if(key[i]==_key)
{
val[i]+=_val;
return ;
}
key[size]=_key;
val[size]=_val;
next[size]=head[p];
head[p]=size++;
}
}hm[2];

void init()//初始化
{
ex=0;
mem(maze, 0);//要把边界全部置零
}

void decode(int *tmp_state, int M, LL key)//把数字解压到数组,M表示矩阵一行的长度
{
for(int i=M;i>=0;--i)//这里用3位表示一个插头的状态
{
tmp_state[i]=key&7;
key>>=3;
}
}
LL encode(int *tmp_state, int M)//最小表示法
{
int cnt=1;
mem(ch,-1);
ch[0]=0;
LL res=0;
for(int i=0;i<=M;++i)
{
if(ch[tmp_state[i]]==-1)
ch[tmp_state[i]]=cnt++;
tmp_state[i]=ch[tmp_state[i]];
res<<=3;
res|=tmp_state[i];
}
return res;
}

void dp_empty(int y, int x, bool now)//必须走的格子
{
for(int i=0;i<hm[now].size;++i)//枚举状态
{
decode(tmp_state, M, hm[now].key[i]);
int left=tmp_state[x-1], up=tmp_state[x];
if(left && up)
{
if(left==up)//最后一个非障碍格子,同一个连通分量合并
{
if(y==ey && x==ex)
{
tmp_state[x-1]=tmp_state[x]=0;
hm[!now].insert(encode(tmp_state, M-(x==M)), hm[now].val[i]);//处理完一行时,把竖着的轮廓线转移到下一行
}
}
else//不在一个强连通分量则合并
{
tmp_state[x-1]=tmp_state[x]=0;
for(int i=0;i<=M;++i)
if(tmp_state[i]==up)//第二个强连通分量的其它点的标记都修改
tmp_state[i]=left;
hm[!now].insert(encode(tmp_state, M-(x==M)), hm[now].val[i]);
}
}
else if((left && !up) || (!left && up))//把路径延伸
{
int t=left?left:up;
if(maze[y][x+1])
{
tmp_state[x-1]=0;
tmp_state[x]=t;
hm[!now].insert(encode(tmp_state, M), hm[now].val[i]);
}
if(maze[y+1][x])
{
tmp_state[x-1]=t;
tmp_state[x]=0;
hm[!now].insert(encode(tmp_state, M-(x==M)), hm[now].val[i]);
}
}
else//没有插头
{
if(maze[y][x+1] && maze[y+1][x])
{
tmp_state[x-1]=tmp_state[x]=13;
hm[!now].insert(encode(tmp_state, M), hm[now].val[i]);
}
}
}
}

void dp_block(int y, int x, bool now)//有障碍
{
for(int i=0;i<hm[now].size;++i)
{
decode(tmp_state, M, hm[now].key[i]);
hm[!now].insert(encode(tmp_state, M-(x==M)), hm[now].val[i]);
}
}

void solve()
{
bool now=0;
hm[now].clear();
hm[now].insert(0, 1);
for(int i=1;i<=N;++i)
for(int j=1;j<=M;++j)
{
hm[!now].clear();
if(maze[i][j])
dp_empty(i, j, now);
else dp_block(i, j, now);
now^=1;
}
LL ans=0;
for(int i=0;i<hm[now].size;++i)
ans+=hm[now].val[i];
printf("%lld\n",ans);
}

int main()
{
while(~scanf("%d%d", &N, &M))
{
init();
for(int i=1;i<=N;++i)
{
char s[MAXN];
scanf("%s",s+1);
for(int j=1;j<=M;++j)
if(s[j]=='.')
{
ey=i;
ex=j;
maze[i][j]=true;
}
}
if(!ex)//没有空格子
{
puts("0");
continue;
}
solve();
}

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: