题目链接: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

Tags: none  (hide tags for unsolved problems)

题意:

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

题解:

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

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

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

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

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

代码如下:

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <algorithm>
  5 #include <vector>
  6 #include <cmath>
  7 #include <queue>
  8 #include <stack>
  9 #include <map>
 10 #include <string>
 11 #include <set>
 12 using namespace std;
 13 typedef long long LL;
 14 const int INF = 2e9;
 15 const LL LNF = 9e18;
 16 const int MOD = 1e9+7;
 17 const int MAXN = 1e5;
 18 const int HASH = 1e4;
 19
 20 int n, m, last_x, last_y;
 21 bool maze[13][13];
 22
 23 struct  //注意哈希表的大小
 24 {
 25     int size, head[HASH], next[MAXN];
 26     LL state[MAXN], sum[MAXN];
 27
 28     void init()
 29     {
 30         size = 0;
 31         memset(head, -1, sizeof(head));
 32     }
 33
 34     void insert(LL status, LL Sum)
 35     {
 36         int u = status%HASH;
 37         for(int i = head[u]; i!=-1; i = next[i])
 38         {
 39             if(state[i]==status)
 40             {
 41                 sum[i] += Sum;
 42                 return;
 43             }
 44         }
 45         state[size] = status;   //头插法
 46         sum[size] = Sum;
 47         next[size] = head[u];
 48         head[u] = size++;
 49     }
 50
 51 }Hash_map[2];
 52
 53 struct
 54 {
 55     int code[13];   //用于记录轮廓线上每个位置的插头状态
 56     LL encode(int m)    //编码:把轮廓线上的信息压缩到一个longlong类型中
 57     {
 58         LL status = 0;
 59         int id[13], cnt = 0;
 60         memset(id, -1, sizeof(id));
 61         id[0] = 0;
 62         for(int i = m; i>=0; i--)   //从高位到低位。为每个连通块重新编号,采用最小表示法。
 63         {
 64             if(id[code[i]]==-1) id[code[i]] = ++cnt;
 65             code[i] = id[code[i]];
 66             status <<= 3;   //编码
 67             status += code[i];
 68         }
 69         return status;
 70     }
 71
 72     void decode(int m, LL status)  //解码:将longlong类型中轮廓线上的信息解码到数组中
 73     {
 74         memset(code, 0, sizeof(code));
 75         for(int i = 0; i<=m; i++)   //从低位到高位
 76         {
 77             code[i] = status&7;
 78             status >>= 3;
 79         }
 80     }
 81
 82     void shift(int m)   //左移:在每次转行的时候都需要执行。
 83     {
 84         for(int i = m-1; i>=0; i--)
 85             code[i+1] = code[i];
 86         code[0] = 0;
 87     }
 88
 89 }Line;
 90
 91 void transfer_blank(int i, int j, int cur)
 92 {
 93     for(int k = 0; k<Hash_map[cur].size; k++)   //枚举上一个格子所有合法的状态
 94     {
 95         LL status = Hash_map[cur].state[k]; //得到状态
 96         LL Sum = Hash_map[cur].sum[k];      //得到数量
 97         Line.decode(m, status);             //对状态进行解码
 98         int up = Line.code[j];         //得到上插头
 99         int left = Line.code[j-1];     //得到下插头
100
101         if(!up && !left)        //没有上、左插头,新建分量
102         {
103             if(maze[i+1][j] && maze[i][j+1])    //如果新建的两个插头所指向的两个格子可行,新建的分量才合法
104             {
105                 Line.code[j] = Line.code[j-1] = 6;  //为新的分量编号,最大的状态才为6
106                 Hash_map[cur^1].insert(Line.encode(m), Sum);
107             }
108         }
109         else if( (left&&!up) || (!left&&up) )   //仅有其中一个插头,延续分量
110         {
111             int line = left?left:up;    //记录是哪一个插头
112             if(maze[i][j+1])    //往右延伸
113             {
114                 Line.code[j-1] = 0;
115                 Line.code[j] = line;
116                 Hash_map[cur^1].insert(Line.encode(m), Sum);
117             }
118             if(maze[i+1][j])        //往下延伸
119             {
120                 Line.code[j-1] = line;
121                 Line.code[j] = 0;
122                 if(j==m) Line.shift(m);
123                 Hash_map[cur^1].insert(Line.encode(m), Sum);
124             }
125         }
126         else    //上、左插头都存在,尝试合并。
127         {
128             if(up!=left)    //如果两个插头属于两个联通分量,那么就合并
129             {
130                 Line.code[j] = Line.code[j-1] = 0;
131                 for(int t = 0; t<=m; t++)   //随便选一个编号最为他们合并后分量的编号
132                     if(Line.code[t]==up)
133                         Line.code[t] = left;
134                 if(j==m) Line.shift(m);
135                 Hash_map[cur^1].insert(Line.encode(m), Sum);
136             }
137             else if(i==last_x && j==last_y) //若两插头同属一个分量,则只能在最后的可行格中合并,否则会出现多个联通分量
138             {
139                 Line.code[j] = Line.code[j-1] = 0;
140                 if(j==m) Line.shift(m);
141                 Hash_map[cur^1].insert(Line.encode(m), Sum);
142             }
143         }
144     }
145 }
146
147 void transfer_block(int i, int j, int cur)
148 {
149     for(int k = 0; k<Hash_map[cur].size; k++)
150     {
151         LL status = Hash_map[cur].state[k]; //得到状态
152         LL Sum = Hash_map[cur].sum[k];      //得到数量
153         Line.decode(m, status);
154         Line.code[j] = Line.code[j-1] = 0;
155         if(j==m) Line.shift(m);
156         Hash_map[cur^1].insert(Line.encode(m), Sum);
157     }
158 }
159
160 int main()
161 {
162     char s[15];
163     while(scanf("%d%d", &n, &m)!=EOF)
164     {
165         memset(maze, false, sizeof(maze));
166         for(int i = 1; i<=n; i++)
167         {
168             scanf("%s", s+1);
169             for(int j = 1; j<=m; j++)
170             {
171                 if(s[j]=='.')
172                 {
173                     maze[i][j] = true;
174                     last_x = i;         //记录最后一个可行格
175                     last_y = j;
176                 }
177             }
178         }
179
180         int cur = 0;
181         Hash_map[cur].init();   //初始化
182         Hash_map[cur].insert(0, 1); //插入初始状态
183         for(int i = 1; i<=n; i++)
184         for(int j = 1; j<=m; j++)
185         {
186             Hash_map[cur^1].init();
187             if(maze[i][j])
188                 transfer_blank(i, j, cur);
189             else
190                 transfer_block(i, j ,cur);
191             cur ^= 1;
192         }
193
194         LL last_status = 0; //最后的轮廓线就是最后一行,且每个位置都没有插头
195         LL ans = Hash_map[cur].size?Hash_map[cur].sum[last_status]:0;
196         printf("%I64d\n", ans);
197     }
198 }

View Code

转载于:https://www.cnblogs.com/DOLFAMINGO/p/8004121.html

URAL1519 Formula 1 —— 插头DP相关推荐

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

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

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

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

  3. POJ-1739 Tony's Tour 插头DP(单条路径)

    题目链接:http://poj.org/problem?id=1739 完全可以用Ural 1519 Formula 1 插头DP(单回路)的代码解决此题,只要把图修改一下: .... ....... ...

  4. Ural 1519. Formula 1 优美的插头DP

    今天早上学了插头DP的思想和最基础的应用,中午就开始敲了,岐哥说第一次写不要看别人代码,利用自己的理解一点点得写出来,这样才锻炼代码能力!于是下午慢慢地构思轮廓,一点点地敲出主体代码,其实是很磨蹭的, ...

  5. 插头DP题目泛做(为了对应WYD的课件)

    题目1:BZOJ 1814 URAL 1519 Formula 1 题目大意:给定一个N*M的棋盘,上面有障碍格子.求一个经过所有非障碍格子形成的回路的数量. 插头DP入门题.记录连通分量. 1 #i ...

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

    转载请注明原文地址:http://www.cnblogs.com/LadyLex/p/7326874.html 最近搞了一下插头DP的基础知识--这真的是一种很锻炼人的题型-- 每一道题的状态都不一样 ...

  7. HDU4084 插头dp

    题意:给定一个图,0是不能放的,然后现在有1X1和1X2方块,最后铺满该图,使得1X1使用次数在C到D之间,1X2次数随便,问有几种放法 思路:插头DP或轮廓线,多加一维DP讨论就可以 注意插头DP状 ...

  8. POJ3133(插头dp)

    传送门:http://poj.org/problem?id=3133 Manhattan Wiring Time Limit: 5000MS   Memory Limit: 65536K       ...

  9. P3272 [SCOI2011]地板(插头DP)

    [题面链接] https://www.luogu.org/problemnew/show/P3272 [题目描述] 有一个矩阵,有些点必须放,有些点不能放,用一些L型的图形放满,求方案数 [题解] ( ...

最新文章

  1. LeetCode简单题之卡牌分组
  2. AVR系列之ADC转换
  3. python基础代码-python基础,python基础代码大全
  4. Linux基础命令--date
  5. linux red hat 安装svn
  6. php strchr 截断,PHP strchr() 函数
  7. android checkbox 选中事件_挖穿Android第四十九天
  8. CSRF 攻击的对象
  9. 利用安卓手机的OTG共享有线网络
  10. testNg官方文档
  11. 动态苹果屏保壁纸软件Brooklyn Mac免费版
  12. 身份证前六位对应代码
  13. 如何在linux上下载各种常用安卓应用
  14. C/C++多线程面试题
  15. SpringCloudAlibaba电商项目实战
  16. 文件上传 webshell 各类型 一句话木马 图片马 制作 教程
  17. 混淆矩阵 灵敏度 特异度 阳性预测值 阴性预测值 阳性似然比 阴性似然比
  18. 计算机秋招必备!杭州互联网大厂企业整理清单!
  19. Windows 硬盘安装 Ubuntu 教程
  20. docker监控方案

热门文章

  1. 机器学习线性回归学习心得_机器学习中的线性回归
  2. 原来信用卡肉这么肥,怪不得银行天天给你发短信叫你办理
  3. 从身体扮演到身份确认:谈谈EMI的人体自拍作品
  4. 北京有个可以躺的酒吧——床吧(BED)
  5. 安卓SlidingDrawer
  6. android 文件公有存储,如何将文件写入Android中的外部公共存储,以便从Windows中看到它们?...
  7. linux关机_强制关机对电脑有什么危害?
  8. matlab二项式拟合,数据回归分析和拟合的Matlab实现
  9. keras如何保存模型
  10. 如何寻找无序数组中的第K大元素?