题目链接:https://vjudge.net/problem/URAL-1519

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

input output
4 4
**..
....
....
....
2
4 4
....
....
....
....
6
Problem Author: Nikita Rybak, Ilya Grebnov, Dmitry Kovalioff
Problem Source: Timus Top Coders: Third Challenge
 

题意:

用一个回路去走完所有的空格,问有多少种情况?

题解:

1.学习插头DP的必经之路:《基于连通性状态压缩的动态规划问题》

2.HDU1693 Eat the Trees 这题的加强版。

3.相对于HDU1693,由于此题限制了只能用一个回路,所以在处理的时候,需要记录轮廓线上,每个插头分别属于哪个连通分量的,以此避免形成多个回路。

4.由于m<=12,故连通分量最多为12/2 = 6个,再加上没有插头的情况,所以轮廓线上每个位置的状态共有7种,为了加快速度,我们采用8进制对其进行压缩。

5.对于一条轮廓线,最多有:8^(12+1)种状态,所以直接用数组进行存储或者直接枚举所以状态是不可行的。但我们知道其中有许多状态是无效的,所以我们采用哈希表来存在有效状态,即能解决空间有限的问题,还能减少直接枚举所需要的时间花费。

代码如下:

 #include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <string>
#include <set>
using namespace std;
typedef long long LL;
const int INF = 2e9;
const LL LNF = 9e18;
const int MOD = 1e9+;
const int MAXN = 1e5;
const int HASH = 1e4; int n, m, last_x, last_y;
bool maze[][]; struct //注意哈希表的大小
{
int size, head[HASH], next[MAXN];
LL state[MAXN], sum[MAXN]; void init()
{
size = ;
memset(head, -, sizeof(head));
} void insert(LL status, LL Sum)
{
int u = status%HASH;
for(int i = head[u]; i!=-; i = next[i])
{
if(state[i]==status)
{
sum[i] += Sum;
return;
}
}
state[size] = status; //头插法
sum[size] = Sum;
next[size] = head[u];
head[u] = size++;
} }Hash_map[]; struct
{
int code[]; //用于记录轮廓线上每个位置的插头状态
LL encode(int m) //编码:把轮廓线上的信息压缩到一个longlong类型中
{
LL status = ;
int id[], cnt = ;
memset(id, -, sizeof(id));
id[] = ;
for(int i = m; i>=; i--) //从高位到低位。为每个连通块重新编号,采用最小表示法。
{
if(id[code[i]]==-) id[code[i]] = ++cnt;
code[i] = id[code[i]];
status <<= ; //编码
status += code[i];
}
return status;
} void decode(int m, LL status) //解码:将longlong类型中轮廓线上的信息解码到数组中
{
memset(code, , sizeof(code));
for(int i = ; i<=m; i++) //从低位到高位
{
code[i] = status&;
status >>= ;
}
} void shift(int m) //左移:在每次转行的时候都需要执行。
{
for(int i = m-; i>=; i--)
code[i+] = code[i];
code[] = ;
} }Line; void transfer_blank(int i, int j, int cur)
{
for(int k = ; k<Hash_map[cur].size; k++) //枚举上一个格子所有合法的状态
{
LL status = Hash_map[cur].state[k]; //得到状态
LL Sum = Hash_map[cur].sum[k]; //得到数量
Line.decode(m, status); //对状态进行解码
int up = Line.code[j]; //得到上插头
int left = Line.code[j-]; //得到下插头 if(!up && !left) //没有上、左插头,新建分量
{
if(maze[i+][j] && maze[i][j+]) //如果新建的两个插头所指向的两个格子可行,新建的分量才合法
{
Line.code[j] = Line.code[j-] = ; //为新的分量编号,最大的状态才为6
Hash_map[cur^].insert(Line.encode(m), Sum);
}
}
else if( (left&&!up) || (!left&&up) ) //仅有其中一个插头,延续分量
{
int line = left?left:up; //记录是哪一个插头
if(maze[i][j+]) //往右延伸
{
Line.code[j-] = ;
Line.code[j] = line;
Hash_map[cur^].insert(Line.encode(m), Sum);
}
if(maze[i+][j]) //往下延伸
{
Line.code[j-] = line;
Line.code[j] = ;
if(j==m) Line.shift(m);
Hash_map[cur^].insert(Line.encode(m), Sum);
}
}
else //上、左插头都存在,尝试合并。
{
if(up!=left) //如果两个插头属于两个联通分量,那么就合并
{
Line.code[j] = Line.code[j-] = ;
for(int t = ; t<=m; t++) //随便选一个编号最为他们合并后分量的编号
if(Line.code[t]==up)
Line.code[t] = left;
if(j==m) Line.shift(m);
Hash_map[cur^].insert(Line.encode(m), Sum);
}
else if(i==last_x && j==last_y) //若两插头同属一个分量,则只能在最后的可行格中合并,否则会出现多个联通分量
{
Line.code[j] = Line.code[j-] = ;
if(j==m) Line.shift(m);
Hash_map[cur^].insert(Line.encode(m), Sum);
}
}
}
} void transfer_block(int i, int j, int cur)
{
for(int k = ; k<Hash_map[cur].size; k++)
{
LL status = Hash_map[cur].state[k]; //得到状态
LL Sum = Hash_map[cur].sum[k]; //得到数量
Line.decode(m, status);
Line.code[j] = Line.code[j-] = ;
if(j==m) Line.shift(m);
Hash_map[cur^].insert(Line.encode(m), Sum);
}
} int main()
{
char s[];
while(scanf("%d%d", &n, &m)!=EOF)
{
memset(maze, false, sizeof(maze));
for(int i = ; i<=n; i++)
{
scanf("%s", s+);
for(int j = ; j<=m; j++)
{
if(s[j]=='.')
{
maze[i][j] = true;
last_x = i; //记录最后一个可行格
last_y = j;
}
}
} int cur = ;
Hash_map[cur].init(); //初始化
Hash_map[cur].insert(, ); //插入初始状态
for(int i = ; i<=n; i++)
for(int j = ; j<=m; j++)
{
Hash_map[cur^].init();
if(maze[i][j])
transfer_blank(i, j, cur);
else
transfer_block(i, j ,cur);
cur ^= ;
} LL last_status = ; //最后的轮廓线就是最后一行,且每个位置都没有插头
LL ans = Hash_map[cur].size?Hash_map[cur].sum[last_status]:;
printf("%I64d\n", ans);
}
}

URAL1519 Formula 1 —— 插头DP的更多相关文章

  1. [URAL1519] Formula 1 [插头dp入门]

    题面: 传送门 思路: 插头dp基础教程 先理解一下题意:实际上就是要你求这个棋盘中的哈密顿回路个数,障碍不能走 看到这个数据范围,还有回路处理,就想到使用插头dp来做了 观察一下发现,这道题因为都是 ...

  2. 【BZOJ1814】Ural 1519 Formula 1 插头DP

    [BZOJ1814]Ural 1519 Formula 1 题意:一个 m * n 的棋盘,有的格子存在障碍,求经过所有非障碍格子的哈密顿回路个数.(n,m<=12) 题解:插头DP板子题,刷板 ...

  3. 【Ural】1519. Formula 1 插头DP

    [题目]1519. Formula 1 [题意]给定n*m个方格图,有一些障碍格,求非障碍格的哈密顿回路数量.n,m<=12. [算法]插头DP [题解]<基于连通性状态压缩的动态规划问题 ...

  4. bzoj1814 Ural 1519 Formula 1(插头dp模板题)

    1814: Ural 1519 Formula 1 Time Limit: 1 Sec  Memory Limit: 64 MBSubmit: 924  Solved: 351[Submit][Sta ...

  5. bzoj 1814 Ural 1519 Formula 1 ——插头DP

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1814 普通的插头 DP .但是调了很久.注意如果合并两个 1 的话,不是 “把向右第一个 2 ...

  6. Ural 1519 Formula 1 插头DP

    这是一道经典的插头DP单回路模板题. 用最小表示法来记录连通性,由于二进制的速度,考虑使用8进制. 1.当同时存在左.上插头的时候,需要判断两插头所在连通块是否相同,若相同,只能在最后一个非障碍点相连 ...

  7. URAL Formula 1 ——插头DP

    [题目分析] 一直听说这是插头DP入门题目. 难到爆炸. 写了2h,各种大常数,ural垫底. [代码] #include <cstdio> #include <cstring> ...

  8. bzoj 1814 Ural 1519 Formula 1 插头DP

    1814: Ural 1519 Formula 1 Time Limit: 1 Sec  Memory Limit: 64 MBSubmit: 942  Solved: 356[Submit][Sta ...

  9. BZOJ1814: Ural 1519 Formula 1(插头Dp)

    Description Regardless of the fact, that Vologda could not get rights to hold the Winter Olympic gam ...

随机推荐

  1. bzoj 1818 Cqoi2010 内部白点 扫描线

    [Cqoi2010]内部白点 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 1126  Solved: 530[Submit][Status][Disc ...

  2. bzoj1975: [Sdoi2010]魔法猪学院【k短路&A*算法】

    1975: [Sdoi2010]魔法猪学院 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 2446  Solved: 770[Submit][Statu ...

  3. Android 获取屏幕事件的坐标

    通常情况下我们只能获取当前Activity的画面坐标,那有时候我们需要做到一种类似于c++ hook的后台运行程序能够监听到前台用户的操作并记录下来,往往这类程序都是为自动化测试服务的. Androi ...

  4. Entity Farmework领域建模方式 3种编程方式

    一个业务领域由各个实体和各个相互关联且有格子的属性和行为的实体组成,每个实体都有其状态和验证规则需要维护,Entity Framework (后面简称EF)实体框架设计的出现是为了允许开发人员着重关注 ...

  5. 【Java TCP/IP Socket】应用程序协议中消息的成帧与解析(含代码)

    程序间达成的某种包含了信息交换的形式和意义的共识称为协议,用来实现特定应用程序的协议叫做应用程序协议.大部分应用程序协议是根据由字段序列组成的离散信息定义的,其中每个字段中都包含了一段以位序列编码(即 ...

  6. Angular 组件通讯、生命周期钩子 小结

    本文为原创,转载请注明出处: cnzt       文章:cnzt-p http://www.cnblogs.com/zt-blog/p/7986858.html http://www.cnblogs ...

  7. Java 利用DFA算法 屏蔽敏感词

    原文:http://www.open-open.com/code/view/1435214601278 import java.io.BufferedReader; import java.io.Fi ...

  8. 聊聊Code Review

    转载:https://richardcao.me/2016/09/30/Talk-About-Codereview/ 最近思考一个问题,如何进行高效的codereview,有没有好的工具可以使用,于是 ...

  9. flask如何使模板返回大文件,又不消耗大量内存

    当我们要往客户端发送大量的数据,比如一个大文件时,将它保存在内存中再一次性发到客户端开销很大.比较好的方式是使用流,本篇就要介绍怎么在Flask中通过流的方式来将响应内容发送给客户端.此外,我们还会演 ...

  10. TCP/IP Protocol Architecture

    原文: https://technet.microsoft.com/en-sg/library/cc958821.aspx 1. 主机到网络层 2.网络互连层(互连这个翻译好) ----------- ...