POJ - 3057 Evacuation 二分图 个人理解

题目:http://poj.org/problem?id=3057

题意:给你一张图 X表示墙,  . 表示人 ,D表示门,D只可能出现在图的图的最外面四条边上,单位时间内 人可以移动一格,但不能移动到墙上,D每个单位时间可以通过一个人,问多久人可以全跑。(建议看原题,这个大意有毒。。)

思路:

题目是让我们找最短时间,可是二分图没办法求时间,因此我们可以将时间融入到顶点里面。

每扇门每次可以出一个人,我们可以将每个时刻的门和该时刻所有可以到达门的人都建立一条边,跑二分图解决。

比如 

XDXXX

X..XXX

XXXXX

在1时刻,坐标为(2,2) 的人可以和时刻1的门建边,在2时刻,坐标为(2,2),(2,3)都可以和时刻2的门建边,一共3条边。我们先记录人的总数,跑二分图的时候一旦边的数量小于人的总数,则说明不可以逃脱。

那么对于这道题还有几处细节。

1.我如何通过边的数量得到时间的信息。

我们在处理顶点的时候可以特殊处理一下。假如一共有3个门那么我们可以用0,1,2 分别代表时刻1的第一个门,第二个门,第三个们; 用3,4,5代表时刻2的第一个门,第二个门,第三个门。跑二分图就遍历这个编号跑dfs,然后边等于人的数量的时候停下来,结果除以三就是答案了。

2.如何建图(二分图)

我们以跑dfs的那一边为左边,那么左边就是门和时间的结合,右边就是人。

先所有人跑一次bfs,得到每个人到门的最快时间,保存在一个四维数组里面,然后建立人和门的边的下限就是这个时间,接着上限就选最大可能的时间。

还要注意左边和右边的编号不能重复了,因此对于右边编号的时候还要注意加上左边编号的最大值。

3.建议用vector存 门和人 的坐标信息

代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
#include<vector>
#include<algorithm>
using namespace std;
const int INF = 0x3f3f3f3f;
const int maxn1 = 15;
const int maxn2 = 100000;
int n, m;
int dis[maxn1][maxn1][maxn1][maxn1], go[4][2] = { 1,0,-1,0,0,1,0,-1 };
// dis: 前两个存门,后两个存人
char maps[maxn1][maxn1];
vector<int> px, py, dx, dy;  //px= people_x     dx=door_x
vector<int> V[maxn2]; // 二分图用的
int match[maxn2], used[maxn2]; //二分图用的
typedef pair<int, int> P;
void bfs(int a, int b) {
	queue<P> Q;
	queue<int> S;
	Q.push(P(a, b));
	S.push(1);
	while (!Q.empty()) {
		P p = Q.front();
		int step = S.front();
		Q.pop(); S.pop();
		for (int i = 0; i < 4; i++) {
			int xx = p.first + go[i][0], yy = p.second + go[i][1];
			if (xx > 1 && xx < n && yy>1 && yy < m) {
				if (dis[xx][yy][a][b] > step && maps[xx][yy] == '.') { 
					dis[xx][yy][a][b] = step;
					Q.push(P(xx, yy));
					S.push(step + 1);
				}
			}
		}
	}
}
bool dfs(int aim) {
	used[aim] = 1;
	for (int i = 0; i < V[aim].size(); i++) {
		int to = V[aim][i];
		if (match[to] == -1 || !used[match[to]] && dfs(match[to])) {
			match[aim] = to;
			match[to] = aim;
			return 1;
		}
	}
	return false;
}
inline void Connect(int a, int b) {
	V[a].push_back(b);
	V[b].push_back(a);
}
int main() {
	int T; cin >> T;
	while (T--) {
		memset(dis, INF, sizeof(dis));
		px.clear(); py.clear();
		dx.clear(); dy.clear();
		cin >> n >> m;
		int all = n * m;
		char s[maxn1];
		for (int i = 1; i <= n; i++) { 
			scanf("%s", s);
			int end = strlen(s);
			for (int j = 0; j < end; j++) {
				maps[i][j + 1] = s[j];
				if (s[j] == 'D') {
					dx.push_back(i);
					dy.push_back(j + 1);
				}
				else if (s[j] == '.') {
					px.push_back(i);
					py.push_back(j + 1);
				}
			}
		}
		int ds = dx.size(), ps = px.size();  //ds= dx_size = door_x.size         
		for (int i = 0; i < ds; i++) {
			bfs(dx[i], dy[i]);
		}
		int max_d = all * ds;  //max_d = max_door
		int clear_flag = all * ds + ps;
		for (int i = 0; i <= clear_flag; i++) V[i].clear();
		for (int i = 0; i < ds; i++) {
			for (int j = 0; j < ps; j++) {
				if (dis[px[j]][py[j]][dx[i]][dy[i]] != INF) {
					for (int k = dis[px[j]][py[j]][dx[i]][dy[i]]; k <= all; k++) { //k <= ps
						Connect((k - 1)*ds + i, max_d + j);
					}
				}
			}
		}
		if (ps == 0) {
			cout << 0 << endl;
			continue;
		}
		memset(match, -1, sizeof(match));
		int ok = 0;
		int res = 0;
		for (int i = 0; i < max_d; i++) { // 二分图
			memset(used, 0, sizeof(used));
			if (dfs(i)) {
				if (++res == ps) {
					cout << (i / ds) + 1 << endl;
					ok = 1;
					break;
				}
			}
		}
		if (ok == 0) cout << "impossible" << endl;
	}
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值