【BZOJ1189】[HNOI2007]紧急疏散evacuate 动态加边网络流

【BZOJ1189】[HNOI2007]紧急疏散evacuate

Description

发生了火警,所有人员需要紧急疏散!假设每个房间是一个N M的矩形区域。每个格子如果是‘.‘,那么表示这是一块空地;如果是‘X‘,那么表示这是一面墙,如果是‘D‘,那么表示这是一扇门,人们可以从这儿撤出房间。已知门一定在房间的边界上,并且边界上不会有空地。最初,每块空地上都有一个人,在疏散的时候,每一秒钟每个人都可以向上下左右四个方向移动一格,当然他也可以站着不动。疏散开始后,每块空地上就没有人数限制了(也就是说每块空地可以同时站无数个人)。但是,由于门很窄,每一秒钟只能有一个人移动到门的位置,一旦移动到门的位置,就表示他已经安全撤离了。现在的问题是:如果希望所有的人安全撤离,最短需要多少时间?或者告知根本不可能。

Input

输入文件第一行是由空格隔开的一对正整数N与M,3<=N <=20,3<=M<=20,以下N行M列描述一个N M的矩阵。其中的元素可为字符‘.‘、‘X‘和‘D‘,且字符间无空格。

Output

只有一个整数K,表示让所有人安全撤离的最短时间,如果不可能撤离,那么输出‘impossible‘(不包括引号)。

Sample Input

5 5
XXXXX
X...D
XX.XX
X..XX
XXDXX

Sample Output

3

题解:这题网上好多题解都过不了,自己也狂WA不止,还好有小号交题大法~

先以每个门为起点进行BFS,将每个门按照时间拆点,设i号位置到j号门的距离为dis,则

i -> (j,dis) 容量1
S -> i 容量1

然后枚举时间,动态加边,设当前时间为tim,则

(j,tim-1) -> (j,tim) 容量∞
(j,tim) -> T 容量1

然后注意BFS的时候只能访问‘.‘的点,不能访问其他的门

#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
#define tx (i+dx[k])
#define ty (j+dy[k])
#define P(A,B) ((A-1)*m+B)
using namespace std;
int to[4000010],next[4000010],val[4000010],head[200000],d[200000],dis[30][30];
int dx[]={1,0,-1,0},dy[]={0,1,0,-1};
int n,m,cnt,S,T,ans,tim,tot,dor;
char str[30][30];
queue<int> q,qx,qy;
int dfs(int x,int mf)
{
	if(x==T)	return mf;
	int i,temp=mf,k;
	for(int i=head[x];i!=-1;i=next[i])
	{
		if(d[to[i]]==d[x]+1&&val[i])
		{
			k=dfs(to[i],min(temp,val[i]));
			if(!k)	d[to[i]]=0;
			val[i]-=k,val[i^1]+=k,temp-=k;
			if(!temp)	break;
		}
	}
	return mf-temp;
}
int bfs()
{
	memset(d,0,sizeof(d));
	while(!q.empty())	q.pop();
	int i,u;
	q.push(S),d[S]=1;
	while(!q.empty())
	{
		u=q.front(),q.pop();
		for(i=head[u];i!=-1;i=next[i])
		{
			if(!d[to[i]]&&val[i])
			{
				d[to[i]]=d[u]+1;
				if(to[i]==T)	return 1;
				q.push(to[i]);
			}
		}
	}
	return 0;
}
void add(int a,int b,int c)
{
	to[cnt]=b,val[cnt]=c,next[cnt]=head[a],head[a]=cnt++;
	to[cnt]=a,val[cnt]=0,next[cnt]=head[b],head[b]=cnt++;
}
void getdis(int x,int y)
{
	memset(dis,0,sizeof(dis));
	qx.push(x),qy.push(y);
	int i,j,k;
	while(!qx.empty())
	{
		i=qx.front(),j=qy.front(),qx.pop(),qy.pop();
		for(k=0;k<4;k++)	if(!dis[tx][ty]&&tx&&tx<=n&&ty&&ty<=m&&str[tx][ty-1]==‘.‘)
		{
			dis[tx][ty]=dis[i][j]+1;
			add(P(tx,ty),P(x,y)+(dis[tx][ty]-1)*n*m,1);
			qx.push(tx),qy.push(ty);
		}
	}
}
int main()
{
	scanf("%d%d",&n,&m);
	int i,j,k;
	S=0,T=n*m*n*m+1;
	memset(head,-1,sizeof(head));
	for(i=1;i<=n;i++)	scanf("%s",str[i]);
	for(i=1;i<=n;i++)	for(j=1;j<=m;j++)
	{
		if(str[i][j-1]==‘.‘)	tot++,add(S,P(i,j),1);
		if(str[i][j-1]==‘D‘)	getdis(i,j);
	}
	while(ans<tot)
	{
		if(tim>=n*m)
		{
			printf("impossible");
			return 0;
		}
		for(i=1;i<=n;i++)	for(j=1;j<=m;j++)
		{
			if(str[i][j-1]==‘D‘)
			{
				add(P(i,j)+tim*n*m,T,1);
				if(tim)		add(P(i,j)+(tim-1)*n*m,P(i,j)+tim*n*m,1<<30);
			}
		}
		tim++;
		while(bfs())	ans+=dfs(S,1<<30);
	}
	printf("%d",tim);
	return 0;
}
时间: 2024-08-02 10:29:58

【BZOJ1189】[HNOI2007]紧急疏散evacuate 动态加边网络流的相关文章

BZOJ1189: [HNOI2007]紧急疏散evacuate 二分+最大流

1189: [HNOI2007]紧急疏散evacuate Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 1132  Solved: 412[Submit][Status][Discuss] Description 发生了火警,所有人员需要紧急疏散!假设每个房间是一个N M的矩形区域.每个格子如果是'.',那么表示这是一块空地:如果是'X',那么表示这是一面墙,如果是'D',那么表示这是一扇门,人们可以从这儿撤出房间.已知门一定在房间的边界上,并且

bzoj千题计划132:bzoj1189: [HNOI2007]紧急疏散evacuate

http://www.lydsy.com/JudgeOnline/problem.php?id=1189 二分答案 源点向人连边,流量为1 门拆为mid个点,同一个门的第j个点向第j+1个点连边,流量为inf 若第i个人第k秒到达第j个门,第i个人向第j个门拆出的第k个点连边,流量为1 所有门向汇点连边,流量为1 用ISAP写的,真快,也真TM长 #include<queue> #include<cstdio> #include<algorithm> #include&

BZOJ1189 HNOI2007 紧急疏散evacuate 网络流+BFS+二分法

题意:一个N M的矩形区域.格子如果是'.',那么表示这是一块空地:如果是'X',那么表示这是一面墙,如果是'D',那么表示这是一扇门,人们可以从这儿撤出房间.已知门一定在房间的边界上,并且边界上不会有空地.每块空地上都有一个人,在疏散的时候,每一秒钟每个人都可以向上下左右四个方向移动一格,当然他也可以站着不动.疏散开始后,每块空地上就没有人数限制.每一秒钟只能有一个人移动到门的位置,一旦移动到门的位置,就表示他已经安全撤离了.求所有的人安全撤离,最短需要多少时间 题解: 好神啊这题…… 首先我

bzoj1189: [HNOI2007]紧急疏散evacuate

Description 发生了火警,所有人员需要紧急疏散!假设每个房间是一个N M的矩形区域.每个格子如果是'.',那么表示这是一块空地:如果是'X',那么表示这是一面墙,如果是'D',那么表示这是一扇门,人们可以从这儿撤出房间.已知门一定在房间的边界上,并且边界上不会有空地.最初,每块空地上都有一个人,在疏散的时候,每一秒钟每个人都可以向上下左右四个方向移动一格,当然他也可以站着不动.疏散开始后,每块空地上就没有人数限制了(也就是说每块空地可以同时站无数个人).但是,由于门很窄,每一秒钟只能有

【BZOJ】1189: [HNOI2007]紧急疏散evacuate(二分+bfs+网络流)

http://www.lydsy.com/JudgeOnline/problem.php?id=1189 表示完全不会QAQ.... 于是膜拜题解orz 二分时间........... 于是转换成判定性问题:即如何在有限时间内通过. 假设当前有t时间可供通过...那么每一个门最多能通过t个人........ 然后将所有能够到达门的点连边,容量为无限.... 然后源向每个点可行点连边..容量1.. 然后每个门向汇连边..容量为t.. 然后判断即可.... (一开始bfs写错了啊QAQ.. #inc

【枚举】【二分答案】【分块答案】【BFS】【最大流】【Dinic】bzoj1189 [HNOI2007]紧急疏散evacuate

[法一]枚举Time(0~N*M): S->'.'(1); 'D'->T(Time); '.'->'D'(dis(用BFS预处理,注意一旦到达'D',BFS就不能继续扩展了,注意dis的初值0x7f)<=Time ? 1 : 0); 判断是否满流; #include<cstdio> #include<cstring> #include<algorithm> #include<queue> using namespace std; #d

[HNOI2007]紧急疏散EVACUATE (湖南2007年省选)

[HNOI2007]紧急疏散EVACUATE 题目描述 发生了火警,所有人员需要紧急疏散!假设每个房间是一个N M的矩形区域.每个格子如果是'.',那么表示这是一块空地:如果是'X',那么表示这是一面墙,如果是'D',那么表示这是一扇门,人们可以从这儿撤出房间.已知门一定在房间的边界上,并且边界上不会有空地.最初,每块空地上都有一个人,在疏散的时候,每一秒钟每个人都可以向上下左右四个方向移动一格,当然他也可以站着不动.疏散开始后,每块空地上就没有人数限制了(也就是说每块空地可以同时站无数个人).

BZOJ 1189: [HNOI2007]紧急疏散evacuate( BFS + 二分答案 + 匈牙利 )

我们可以BFS出每个出口到每个人的最短距离, 然后二分答案, 假设当前答案为m, 把一个出口拆成m个表示m个时间, 点u到出口v的距离为d, 那么u->v的[d, m]所有点连边, 然后跑匈牙利去check就行了...其实这道题挺好想但是码量还是挺大的.... ----------------------------------------------------------------------------- #include<cstdio> #include<cstring&

AC日记——[HNOI2007]紧急疏散evacuate bzoj 1189

[HNOI2007]紧急疏散evacuate 思路: 处理每个人到门的最短路: 然后二分答案: s向人连边流量1: 人向门拆分后的点连边流量1(拆成400,前一个点连当前点流量INF): 然后门向t连边流量二分的答案: 如果最后流量等于人的个数,则true: 来,上代码: #include <queue> #include <cstdio> #include <cstring> #include <iostream> #include <algorit