fzu1977之插头DP

 Problem 1977 Pandora adventure

Accept: 354    Submit: 1177

Time Limit: 1000 mSec    Memory Limit : 32768 KB

 Problem Description

The pollution of the earth is so serious that people can not survive any more. Fortunately, people have found a new planet that maybe has life, and we call it "Pandora Planet".

Leonardo Da Vinci is the only astronaut on the earth. He will be sent to the Pandora Planet to gather some plant specimens and go back. The plant specimen is important to the people to decide whether the planet is fit to live or not.

Assuming that Da Vinci can only move in an N×M grid. The positions of the plant specimens he wants to collect are all marked by the satellite. His task is to find a path to collect all the plant specimens and return to the spaceship. There are some savage
beasts in the planet. Da Vinci can not investigate the grid with the savage beast. These grids are also marked by the satellite. In order to save time Da Vinci could only visit each grid exactly once and also return to the start grid, that is, you can not
visit a grid twice except the start grid. You should note that you can choose any grid as the start grid.

Now he wants to know the number of different paths he can collect all the plant specimens. We only care about the path and ignore where the start grid is, so the two paths in Figure 1 are considered as the same.


Figure 1

 Input

The first line of the input contains an integer T (T≤100), indicating the number of cases. Each case begins with a line containing two integers N and M (1≤N, M≤12), the size of the planet is N×M. Each of the following N lines contains M characters Gij(1≤i≤N,
1≤j≤M), Gij denotes the status of the grid in row i and column j, where ‘X‘ denotes the grid with savage beast, ‘*‘ denotes the safe grid that you can decide to go or not, ‘O‘ denotes the plant specimen you should collect. We guarantee that
there are at least three plant specimens in the map.

 Output

For each test case, print a line containing the test case number (beginning with 1) and the number of different paths he can collect all the plant specimens. You can make sure that the answer will fit in a 64-bit signed integer.

 Sample Input

22 2OOO*4 4***OXO****O*XX**

 Sample Output

Case 1: 1Case 2: 7

题意:输入一个n*m的地图。当中字母‘O‘的点是必走点。字母‘X‘是不能走的点,字母‘*‘是可走可不走的点。求经过全部点‘O‘的不同回路路径个数(路线同样起始点不同视为同样路径)。

分析:因为须要求回路个数,所以能想到是插头DP,本题在最简单的求回路个数上添加一些必走的点和可走的点

对于可走的点仅仅要在插头DP的时候假设p=q=0且该点能够不走,则就添加一种不走的状态:

if(!p && !q){
	if(mp[i][j] == ‘*‘)HashCalState(s,num);
	if(mp[i][j+1] == ‘X‘ || mp[i+1][j] == ‘X‘)continue;
	s=s+(1<<bit[j-1])+2*(1<<bit[j]);
	HashCalState(s,num);
}

而问题在于假设求经过全部‘O‘的回路?

如果求经过全部可经过点的回路时碰到p=1,q=2合并连通块

则发生这样的情况必须是最后一个可走的点。这样全部可走的点才会连通

在这里也是一样,仅仅要记录最后一个必走的点‘O就可以

在碰到p=1,q=2合并连通块的时候。这时候仅仅需推断这个点是不是最后一个“O‘点而且能够连成环

推断连成环既已决策的点没有插头连着未决策的点,这样全部已决策的点连着必成环

if(p == 1 && q == 2){//成环
	if(i == ex && j<ey)continue;
	if(i<ex)continue;
	s=s-(1<<bit[j-1])-2*(1<<bit[j]);//求解其它已决策的点十是否和未决策的点相连
	if(!s)sum+=num;
}

其它就是插头DP模版了

採用邻接表寻找状态时head数组能够开小点,head的大小和效率关系非常大

另外本题好坑爹!

。!用long long WA,用__int64 AC,平白无故查错一晚上+一中午

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <queue>
#include <algorithm>
#include <map>
#include <cmath>
#include <iomanip>
#define INF 99999999
typedef __int64 LL;
using namespace std;

const int MAX=30000+10;
const int maxn=50000+10;
const int N=12+10;
int n,m,index,size[2],bit[N];
int ex,ey;
int head[MAX],next[maxn];
LL dp[2][maxn],state[2][maxn],sum;
char mp[N][N];

void HashCalState(LL s,LL num){
	int pos=s%MAX;
	for(int i=head[pos];i != -1;i=next[i]){
		if(state[index][i] == s){
			dp[index][i]+=num;
			return;
		}
	}
	state[index][size[index]]=s;
	dp[index][size[index]]=num;
	//头插法
	next[size[index]]=head[pos];
	head[pos]=size[index]++;
}

void DP(){
	sum=0;
	index=0;
	size[index]=1;
	state[index][0]=0;
	dp[index][0]=1;
	for(int i=1;i<=n;++i){
		for(int k=0;k<size[index];++k)state[index][k]<<=2;
		for(int j=1;j<=m;++j){
			index=index^1;
			memset(head,-1,sizeof head);
			size[index]=0;
			for(int k=0;k<size[index^1];++k){
				LL s=state[index^1][k];
				LL num=dp[index^1][k];
				int p=(s>>bit[j-1])%4;
				int q=(s>>bit[j])%4;
				if(mp[i][j] == ‘X‘){//须要绕过
					if(!p && !q)HashCalState(s,num);
				}else if(!p && !q){
					if(mp[i][j] == ‘*‘)HashCalState(s,num);
					if(mp[i][j+1] == ‘X‘ || mp[i+1][j] == ‘X‘)continue;
					s=s+(1<<bit[j-1])+2*(1<<bit[j]);
					HashCalState(s,num);
				}else if(!p && q){
					if(mp[i][j+1] != ‘X‘)HashCalState(s,num);
					if(mp[i+1][j] != ‘X‘){
						s=s+q*(1<<bit[j-1])-q*(1<<bit[j]);
						HashCalState(s,num);
					}
				}else if(p && !q){
					if(mp[i+1][j] != ‘X‘)HashCalState(s,num);
					if(mp[i][j+1] != ‘X‘){
						s=s-p*(1<<bit[j-1])+p*(1<<bit[j]);
						HashCalState(s,num);
					}
				}else if(p == 1 && q == 1){
					int b=1;
					for(int t=j+1;t<=m;++t){
						int v=(s>>bit[t])%4;
						if(v == 1)++b;
						if(v == 2)--b;
						if(!b){
							s=s+(1<<bit[t])-2*(1<<bit[t]);
							break;
						}
					}
					s=s-(1<<bit[j-1])-(1<<bit[j]);
					HashCalState(s,num);
				}else if(p == 2 && q == 2){
					int b=1;
					for(int t=j-2;t>=0;--t){
						int v=(s>>bit[t])%4;
						if(v == 2)++b;
						if(v == 1)--b;
						if(!b){
							s=s-(1<<bit[t])+2*(1<<bit[t]);
							break;
						}
					}
					s=s-2*(1<<bit[j-1])-2*(1<<bit[j]);
					HashCalState(s,num);
				}else if(p == 1 && q == 2){//成环
					if(i == ex && j<ey)continue;
					if(i<ex)continue;
					s=s-(1<<bit[j-1])-2*(1<<bit[j]);
					if(!s)sum+=num;
				}else if(p == 2 && q == 1){
					s=s-2*(1<<bit[j-1])-(1<<bit[j]);
					HashCalState(s,num);
				}
			}
		}
	}
}

int main(){
	for(int i=0;i<N;++i)bit[i]=i<<1;
	int t,num=0;
	scanf("%d",&t);
	while(t--){
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;++i)scanf("%s",mp[i]+1);
		ex=ey=-1;
		for(int i=1;i<=n;++i){
			for(int j=1;j<=m;++j)if(mp[i][j] == ‘O‘)ex=i,ey=j;
			mp[i][m+1]=‘X‘;
		}
		for(int j=1;j<=m;++j)mp[n+1][j]=‘X‘;
		DP();
		if(ex == -1)++sum;
		printf("Case %d: %I64d\n",++num,sum);
	}
	return 0;
}
/*
100
12 12
************
************
************
************
************
************
************
************
************
************
************
************
*/ 
时间: 2024-11-17 08:06:14

fzu1977之插头DP的相关文章

初探插头dp

开学那个月学了点新东西,不知道还记不记得了,mark一下 感觉cdq的论文讲的很详细 题主要跟着kuangbin巨做了几道基础的 http://www.cnblogs.com/kuangbin/archive/2012/10/02/2710343.html 还有几道没做,留着坑 感觉广义括号表示法虽然神奇,但一般最小表示法就够用了吧,感觉最小表示法更直观一点 hdu1693 1 #include<cstdio> 2 #include<iostream> 3 #include<

插头dp

对于网格中的dp可以用轮廓线,如果有一些格子不能走就可以用插头dp了. bzoj2331 地板 题目大意:用L型铺地n*m,有一些格子不能铺,求方案数. 思路:fi[i][j][s]表示铺到(i,j),轮廓线状态s,0表示没有插头,1表示插头没拐弯,2表示插头拐弯了,手动枚举转移. 注意:(1)按四进制好写: (2)因为实际状态和四进制的差很多,所以用hash表来存储,防止mle和tle,同时使用滚动数组. #include<iostream> #include<cstdio> #

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

转载请注明原文地址:http://www.cnblogs.com/LadyLex/p/7326874.html 最近搞了一下插头DP的基础知识……这真的是一种很锻炼人的题型…… 每一道题的状态都不一样,并且有不少的分类讨论,让插头DP十分锻炼思维的全面性和严谨性. 下面我们一起来学习插头DP的内容吧! 插头DP主要用来处理一系列基于连通性状态压缩的动态规划问题,处理的具体问题有很多种,并且一般数据规模较小. 由于棋盘有很特殊的结构,使得它可以与“连通性”有很强的联系,因此插头DP最常见的应用要数

插头DP学习

队内没人会插头DP,感觉这个不会不行...所以我还是默默去学了一下, 学了一天,感觉会了一点.对于每一行,一共有j+1个插头,如果是多回路类的题目, 比较简单,可以用1表示有插头,0表示没有插头,这样就可以愉快转移了, 对于当前出来的位置(i,j),与它有关的插头有j-1和j 那么我们可以枚举状态经行转移. 对于单回路的问题....只是了解思想,目前还不会写,太笨了=_= poj 2411 Mondriaan's Dream 题意:用1*2小方块组成n*m矩阵有多少种组成方式 思路:我们从上到下

BZOJ 2595 游览计划(插头DP)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=2595 题意:给出一个数字矩阵.求一个连通块包含所有的数字0且连通块内所有数字之和最小. 思路:对于每个格子,是0则必须要选.那么对于不选的格子(i,j)在什么时候可以不选呢?必须同时满足以下两个条件: (1)(i,j)不是0: (2)(i-1,j)不选或者(i-1,j)选了但是轮廓线上还有别的地方与(i-1,j)是一个连通块. int Pre[105][N],op[105][N]; s

【插头dp】CDOJ1690 这是一道比CCCC简单题难的简单题

最裸的插头dp,可参见大白书. #include<cstdio> #include<cstring> using namespace std; #define MOD 1000000007 int f[2][(1<<5)+10],n,m; int main(){ scanf("%d%d",&n,&m); int cur=0; f[0][(1<<m)-1]=1; for(int i=0;i<n;++i){ for(in

POJ 2411 Mondriaan&#39;s Dream ——状压DP 插头DP

[题目分析] 用1*2的牌铺满n*m的格子. 刚开始用到动规想写一个n*m*2^m,写了半天才知道会有重复的情况. So Sad. 然后想到数据范围这么小,爆搜好了.于是把每一种状态对应的转移都搜了出来. 加了点优(gou)化(pi),然后poj上1244ms垫底. 大概的方法就是考虑每一层横着放的情况,剩下的必须竖起来的情况到下一层取反即可. 然后看了 <插头DP-从入门到跳楼> 这篇博客,怒抄插头DP 然后16ms了,自己慢慢YY了一下,写出了风(gou)流(pi)倜(bu)傥(tong)

HDU 4113 Construct the Great Wall(插头dp)

好久没做插头dp的样子,一开始以为这题是插头,状压,插头,状压,插头,状压,插头,状压,无限对又错. 昨天看到的这题. 百度之后发现没有人发题解,hust也没,hdu也没discuss...在acm-icpc信息站发现难得的一篇题解.不过看到是插头二字之后,代码由于风格太不一样就没看了,自己想了好久,想通了.然后就等到今天才码.... 如果把点看成网格,那就可以实现,没有公共点公共边等限定条件,也显然是插头dp的最短单回路的模型.这是本题的一个难点(当时想到这样是因为,题目要求计算最短周长,显然

BZOJ 2331 地板(插头DP)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=2331 题意:给出一个n*m的地面.有些是障碍.用L型的地板砖铺满.有多少种方案. 思路:用0表示没有插头,用1表示有插头且可以拐弯,用3表示有插头但是不能再拐弯了. 设有m列,轮廓线为[0,m].对于格子(i,j),设左插头x上插头y,那么转移有: (1)x=0,y=0:此时如图1-0,有三种转移,分别是1-1,1-2,1-3; (2)x!=0,y!=0:此时只有当x=y=1时可以转移