首页 > 其他 > 详细

解题报告 之 POJ3057 Evacuation

时间:2015-04-28 23:02:47      阅读:523      评论:0      收藏:0      [点我收藏+]

解题报告 之 POJ3057 Evacuation


Description

Fires can be disastrous, especially when a fire breaks out in a room that is completely filled with people. Rooms usually have a couple of exits and emergency exits, but with everyone rushing out at the same time, it may take a while for everyone to escape. 

You are given the floorplan of a room and must find out how much time it will take for everyone to get out. Rooms consist of obstacles and walls, which are represented on the map by an ‘X‘, empty squares, represented by a ‘.‘ and exit doors, which are represented by a ‘D‘. The boundary of the room consists only of doors and walls, and there are no doors inside the room. The interior of the room contains at least one empty square. 

Initially, there is one person on every empty square in the room and these persons should move to a door to exit. They can move one square per second to the North, South, East or West. While evacuating, multiple persons can be on a single square. The doors are narrow, however, and only one person can leave through a door per second. 

What is the minimal time necessary to evacuate everybody? A person is evacuated at the moment he or she enters a door square.

Input

The first line of the input contains a single number: the number of test cases to follow. Each test case has the following format: 
One line with two integers Y and X, separated by a single space, satisfying 3 <= Y, X <= 12: the size of the room 
Y lines with X characters, each character being either ‘X‘, ‘.‘, or ‘D‘: a valid description of a room

Output

For every test case in the input, the output should contain a single line with the minimal evacuation time in seconds, if evacuation is possible, or "impossible", if it is not. 

Sample Input

3
5 5
XXDXX
X...X
D...X
X...D
XXXXX
5 12
XXXXXXXXXXXX
X..........D
X.XXXXXXXXXX
X..........X
XXXXXXXXXXXX
5 5
XDXXX
X.X.D
XX.XX
D.X.X
XXXDX

Sample Output

3
21
impossible


题目大意:有一个n*m的格子房间,某些格子是柱子X,则不能站人,而 . 表示此处是空地,可以站无限多的人。在周围的格子上有一些门D。现在这个房子着火了,每个空地 . 上有一个人,然后他们每秒可以向上下左右走一格或者原地不动。一个门一秒内只能通过一个人。问全部人逃跑至少需要多少时间,如果无法逃出则输出impossible。


分析:首先需要了解时间动态流模型。如果不明白请参照经典例题:

SGU438 The Glorious Karlutka River

http://blog.csdn.net/maxichu/article/details/45219567


我看了看《挑战》上关于此题的解法,是一种二分图匹配的算法,不是很明白。所以还是用的动态流模型。一开始先让超级源点连接每一个格子,负载为1,表示有一个人。注意每个格子需要拆点,负载为INF,因为格子上可以站无限多的人(其实本不需要拆点,为了配合门我懒得分了于是就都拆了。。。反正空间充足)。然后我们遍历时间,从1开始进行直到150(因为最坏的情况也不会超过144s,150是写着方便)。我们关注现在进行到时刻 t ,在时刻 t 的层次残余流量图中。对于每个空地( i , j ) t,首先将 t 时刻的状态拆点。然后这个空地的周围四个格子上一秒的状态一定可以转换过来,那么就连边。


                                                 int nx = i + mox[k], ny = j + moy[k];
if(nx >= 1 && nx <= n&&ny >= 1 && ny <= m&&map[nx][ny] != ‘X‘)
addedge( (t - 1) * 300 + (nx - 1) * 12 + ny + 150, t * 300 + (i - 1) * 12 + j, INF );


同样对于时刻 t 的门( i , j ) t,首先连接它与超级汇点des。然后这个门周围的四个格子的上一秒的状态也可以转换过来,那么就连边。


int nx = i + mox[k], ny = j + moy[k];
if(nx >= 1 && nx <= n&&ny >= 1 && ny <= m&&map[nx][ny] != ‘X‘)
addedge( (t - 1) * 300 + (nx-1) * 12 + ny + 150, t * 300 + (i - 1) * 12 + j, INF );


其实门与空地状态更新是一样的,主要区别在于拆不拆点。(其实我写的时候发现好像不拆点也行,但是也不想改了,囧)。这一秒连边结束后跑最大流,看看最大流的总和是否已经超过了人数peo,超过就退出。最后根据 t 输出答案即可。


上代码:

#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cstdio>
using namespace std;

const int MAXN = 65010;
const int MAXM = 990000;
const int INF = 0x3f3f3f3f;

struct Edge
{
	int from, to, cap, next;
};

int mox[5] = { 0, -1, 1, 0, 0 };
int moy[5] = { 0, 0, 0, -1, 1 };
Edge edge[MAXM];
int level[MAXN];
char map[15][15];
int head[MAXN];
int src, des, cnt;

void addedge(int from,int to,int cap)
{
	edge[cnt].from = from;
	edge[cnt].to = to;
	edge[cnt].cap = cap;
	edge[cnt].next = head[from];
	head[from] = cnt++;

	swap( from, to );

	edge[cnt].from = from;
	edge[cnt].to = to;
	edge[cnt].cap = 0;
	edge[cnt].next = head[from];
	head[from] = cnt++;
}

int bfs()
{
	memset( level, -1, sizeof level );
	queue<int> q;
	while(!q.empty())
		q.pop();

	level[src] = 0;
	q.push( src );

	while(!q.empty())
	{
		int u = q.front();
		q.pop();

		for(int i = head[u]; i != -1; i = edge[i].next)
		{
			int v = edge[i].to;
			if(edge[i].cap&&level[v] == -1)
			{
				level[v] = level[u] + 1;
				q.push( v );
			}
		}
	}
	return level[des] != -1;
}

int dfs( int u, int f )
{
	if(u == des)
		return f;
	int tem;
	for(int i = head[u]; i != -1; i = edge[i].next)
	{
		int v = edge[i].to;
		if(edge[i].cap > 0 && level[v] == level[u] + 1)
		{
			tem = dfs( v, min( f, edge[i].cap ) );
			if(tem > 0)
			{
				edge[i].cap -= tem;
				edge[i^1].cap += tem;
				return tem;
			}
		}
	}
	level[u] = -1;
		return 0;
}

int Dinic()
{
	int ans = 0, tem;
	while(bfs())
	{
		while((tem = dfs( src, INF ) > 0))
		{
			ans += tem;
		}
	}
	return ans;
}

int main()
{
	int kase;
	cin >> kase;
	int n, m,peo;
	src = 0; des = 65005;
	while(kase--)
	{
		memset( head, -1, sizeof head );
		cnt = 0;
		peo = 0;
		cin >> n >> m;
		for(int i = 1; i <= n; i++)
		{
			for(int j = 1; j <= m; j++)
			{
				cin >> map[i][j];
			}
		}

		for(int i = 1; i <= n; i++)
		{
			for(int j = 1; j <= m; j++)
			{
				if(map[i][j] == '.')
				{
					peo++;
					addedge( src, (i-1) * 12 + j, 1 );
					addedge( (i - 1) * 12 + j, (i - 1) * 12 + j + 150, INF );
				}
			}
		}
		int ans = 0;
		int t;
		for( t = 1; t <= 150; t++) //枚举时间 多次增广
		{

			for(int i = 1; i <= n; i++)
			{
				for(int j = 1; j <= m; j++)
				{
					if(map[i][j] == '.')
					{
						addedge( t * 300 + (i - 1) * 12 + j, t * 300 + (i - 1) * 12 + j + 150, INF );
						for(int k = 0; k <= 4; k++)
						{
							int nx = i + mox[k], ny = j + moy[k];
							if(nx >= 1 && nx <= n&&ny >= 1 && ny <= m&&map[nx][ny] != 'X')
								addedge( (t - 1) * 300 + (nx - 1) * 12 + ny + 150, t * 300 + (i - 1) * 12 + j, INF );
						}
					}
					if(map[i][j] == 'D')
					{
						addedge( t * 300 + (i - 1) * 12 + j, des, 1 );
						for(int k = 0; k <= 4; k++)
						{
							int nx = i + mox[k], ny = j + moy[k];
							if(nx >= 1 && nx <= n&&ny >= 1 && ny <= m&&map[nx][ny] != 'X')
								addedge( (t - 1) * 300 + (nx-1) * 12 + ny + 150, t * 300 + (i - 1) * 12 + j, INF );
						}
					}
				}
			}
			ans += Dinic();
			if(ans >= peo) break;

		}
		if(t > 150) cout << "impossible" << endl;
		else	cout << t << endl;
	}

	return 0;
}

关乎大大提前过五一去泸沽湖的事,我已经报警了,BJ4。。。周四一天课呢!


解题报告 之 POJ3057 Evacuation

原文:http://blog.csdn.net/maxichu/article/details/45340237

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!