【bzoj1814】Ural 1519 Formula 1 插头dp

题目描述

一个 m * n 的棋盘,有的格子存在障碍,求经过所有非障碍格子的哈密顿回路个数。

输入

The first line contains the integer numbers N and M (2 ≤ N, M ≤ 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.

输出

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

样例输入

4 4
**..
....
....
....

样例输出

2



题解

插头dp板子题

具体讲解可以参考  陈丹琦《基于连通性状态压缩的动态规划问题》  以及  Dalao博客  。

我的代码中使用了三进制状态,先使用dfs预处理出所有合法的广义括号序列,用0表示没有插头,1表示左括号插头,2表示右括号插头。处理好状态与编号的对应关系后进行dp,复杂度就只与合法状态数有关了。

时间复杂度 $O(合法状态数·nm^2)$ ,经计算合法状态数不超过42000。而实际上这个时间复杂度远远达不到上限,因为只有两个插头是双左括号或双右括号时才会产生最后的m,且这个m也是不满的。因此可以过。

注意需要long long。

#include <cstdio>
#include <algorithm>
using namespace std;
int m , a[14][14] , v[42000] , w[1600000] , tot , b[14];
long long f[13][13][42000];
char str[14];
void dfs(int p , int c , int now)
{
	if(c < 0 || c > m + 1 - p + 1) return;
	if(p > m + 1)
	{
		v[++tot] = now , w[now] = tot;
		return;
	}
	dfs(p + 1 , c , now);
	dfs(p + 1 , c + 1 , now + b[p - 1]);
	dfs(p + 1 , c - 1 , now + 2 * b[p - 1]);
}
inline int left(int v , int p)
{
	int i , c = 0;
	for(i = p ; ~i ; i -- )
	{
		if(v / b[i] % 3 == 1) c -- ;
		if(v / b[i] % 3 == 2) c ++ ;
		if(!c) return i;
	}
	return -1;
}
inline int right(int v , int p)
{
	int i , c = 0;
	for(i = p ; i <= m ; i ++ )
	{
		if(v / b[i] % 3 == 2) c -- ;
		if(v / b[i] % 3 == 1) c ++ ;
		if(!c) return i;
	}
	return -1;
}
int main()
{
	int n , i , j , k , nn , mm , p , q;
	long long ans = 0;
	scanf("%d%d" , &n , &m);
	for(i = 1 ; i <= n ; i ++ )
	{
		scanf("%s" , str + 1);
		for(j = 1 ; j <= m ; j ++ )
			if(str[j] == ‘.‘)
				a[i][j] = 1 , nn = i , mm = j;
	}
	b[0] = 1;
	for(i = 1 ; i <= m ; i ++ ) b[i] = b[i - 1] * 3;
	dfs(1 , 0 , 0);
	f[1][0][1] = 1;
	for(i = 1 ; i <= n ; i ++ )
	{
		for(j = 1 ; j <= m ; j ++ )
		{
			for(k = 1 ; k <= tot ; k ++ )
			{
				p = v[k] / b[j - 1] % 3 , q = v[k] / b[j] % 3;
				if(!a[i][j])
				{
					if(!p && !q) f[i][j][k] += f[i][j - 1][k];
				}
				else
				{
					if(!p && !q && a[i][j + 1] && a[i + 1][j]) f[i][j][w[v[k] + b[j - 1] + 2 * b[j]]] += f[i][j - 1][k];
					if(!p && q)
					{
						if(a[i][j + 1]) f[i][j][k] += f[i][j - 1][k];
						if(a[i + 1][j]) f[i][j][w[v[k] + q * (b[j - 1] - b[j])]] += f[i][j - 1][k];
					}
					if(p && !q)
					{
						if(a[i + 1][j]) f[i][j][k] += f[i][j - 1][k];
						if(a[i][j + 1]) f[i][j][w[v[k] + p * (b[j] - b[j - 1])]] += f[i][j - 1][k];
					}
					if(p == 1 && q == 1) f[i][j][w[v[k] - b[j - 1] - b[j] - b[right(v[k] , j)]]] += f[i][j - 1][k];
					if(p == 2 && q == 2) f[i][j][w[v[k] - 2 * b[j - 1] - 2 * b[j] + b[left(v[k] , j - 1)]]] += f[i][j - 1][k];
					if(p == 2 && q == 1) f[i][j][w[v[k] - 2 * b[j - 1] - b[j]]] += f[i][j - 1][k];
					if(p == 1 && q == 2 && i == nn && j == mm) ans += f[i][j - 1][k];
				}
			}
		}
		if(i != n)
			for(k = 1 ; k <= tot ; k ++ )
				if(v[k] % 3 == 0)
					f[i + 1][0][k] += f[i][m][w[v[k] / 3]];
	}
	printf("%lld\n" , ans);
	return 0;
}

原文地址:https://www.cnblogs.com/GXZlegend/p/8127185.html

时间: 2024-07-31 21:17:33

【bzoj1814】Ural 1519 Formula 1 插头dp的相关文章

bzoj 1814: Ural 1519 Formula 1 插头dp经典题

用的括号序列,听说比较快. 然并不会预处理,只会每回暴力找匹配的括号. 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #define N 199917 6 #define ll long long 7 #define bp 1<<bit[j-1] 8 #define bq 1<<bit[j] 9 using nam

bzoj1814: Ural 1519 Formula 1 2011-12-20

1814: Ural 1519 Formula 1Time Limit: 1 Sec  Memory Limit: 64 MBSubmit: 263  Solved: 70[Submit][Status][Discuss]DescriptionRegardless of the fact, that Vologda could not get rights to hold the Winter Olympic games of 20**, it is well- known, that the 

【URAL 1519】【插头dp模板】Formula 1

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

bzoj1814: Ural 1519 Formula 1 动态规划 插头dp

dbzoj依然爆炸 题目描述 一个 m * n 的棋盘,有的格子存在障碍,求经过所有非障碍格子的哈密顿回路个数. 输入 The first line contains the integer numbers N and M (2 ≤ N, M ≤ 12). Each of the next N lines contains M characters, which are the corresponding cells of the rectangle. Character "." (f

[URAL 1519] Formula 1 轮廓线DP 括号序列

题意 n * m 的矩形, 有坏点, 问哈密顿回路数量. n, m <= 11 . 分析 1. 确立状态 我们考虑轮廓线DP. 为此, 我们要刻画并量化轮廓线的相关信息: ① 插头是否存在? ② 插头的连通性. 我们发现: 插头一一对应, 且互不相交. 于是考虑使用括号序列刻画轮廓线. 至于量化, 我们将一个括号序列当做一个三进制数即可. 2. 转移 从上一行的最后一个, 转移到当前行的第一个: 位移. 当前格子为坏点: 对于没有插头插到当前点的状态原样复制. 否则: (1) L = # , R

URAL - 1519 Formula 1 (插头DP)

这里写链接内容 刚开始学插头dp好吃力,看了别人的代码有点看不懂,所以就参考了别人的代码,写了注释,希望有帮助 如果看不懂可以问 //下面所说的情况全在论文中的第13页 #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define N 13 #define S1 14000 #define S2 1600000 struct Queue{ int opt; long

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

Ural1519 Formula 1(插头dp)

原题网址:http://acm.timus.ru/problem.aspx?space=1&num=1519 有关插头dp和状态压缩请参考:http://wenku.baidu.com/link?url=AFuYe_EfR5yXMNK0rY-TaLe6LLgKhsOVxBM1RQULxElPrvjQVlO724nUxlXtaDx4aLp7FHIz8AexYiTy06_r4CV5XUs6c9lM5vpz5kDr6HG 详细代码(c++): 1 #include <cstdio> 2 #i

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

题面: 传送门 思路: 先理解一下题意:实际上就是要你求这个棋盘中的哈密顿回路个数,障碍不能走 看到这个数据范围,还有回路处理,就想到使用插头dp来做了 观察一下发现,这道题因为都是回路,所以联通块上方的插头一定两两配对,可以使用括号序列代替最小表示法 分情况讨论一下 情况一:当前格子上方和左方都没有插头 这种时候可以继续,也可以给当前格子加一个下插头一个右插头,相当于一个新的联通分量 情况二:上方有一个下插头,左边没有 这时有两个决策:可以向右转,也可以继续向下,操作就是分别给这个格子一个右插