poj 3057

考虑某一个门,能在时间t从该门逃脱的人,应该是距离该门t以内的人,并且其中只有一人能够从该门逃脱。每个时间和门的二元组,都能确定一个对应的能够从中逃脱的人的集合,而通过计算这个二元组和人组成的二分图的匹配数,我们就可以判断所有人是否都可以逃脱。


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

const int dx[4] = {-1, 0, 0, 1}, dy[4] = {0, -1, 1, 0};

const int MAX_X = 20;
const int MAX_Y = 20;
const int MAX_V = 100000;
int X, Y;
char field[MAX_X][MAX_Y + 1]; //输入

vector<int> dX, dY; // 门的坐标
vector<int> pX, pY; // 人的坐标
vector<int> G[MAX_V]; //保存图
int dist[MAX_X][MAX_Y][MAX_X][MAX_Y]; //每个门到每个人的最短距离

int match[MAX_V];
bool used[MAX_V];


void add_edge(int u, int v) {
	G[u].push_back(v);
	G[v].push_back(u);
}

bool dfs(int v) {
	used[v] = true;
	for (int i = 0; i < G[v].size(); i++) {
		int u = G[v][i], w = match[u];
		if  (w < 0 || !used[w] && dfs(w)) {
			match[u] = v;
			match[v] = u;
			return true;
		}
	}
	return false;
}

//bfs求最短距离
void bfs(int x, int y, int d[MAX_X][MAX_Y]) {
	queue<int> qx, qy;
	d[x][y] = 0;
	qx.push(x);
	qy.push(y);
	while(!qx.empty()) {
		x = qx.front(); qx.pop();
		y = qy.front(); qy.pop();
		for (int i = 0; i < 4; i++) {
			int x2 = dx[i] + x, y2 = dy[i] + y;
			if (0 <= x2 && x2 < X && 0 <= y2 && y2 < Y && field[x2][y2] == '.' && d[x2][y2] < 0) {
				d[x2][y2] = d[x][y] + 1;
				qx.push(x2);
				qy.push(y2);
			}
		}
	}
}

void solve() {
	int n = X * Y;
	dX.clear(); dY.clear();
	pX.clear(); pY.clear();
	for (int i = 0; i < MAX_V; i++)
	G[i].clear();
	memset(dist, -1, sizeof(dist));
	for (int x = 0; x < X; x++) {
		for (int y = 0; y < Y; y++) {
			if (field[x][y] == 'D') {
				dX.push_back(x);
				dY.push_back(y);
				bfs(x, y, dist[x][y]);
			} else if (field[x][y] == '.') {
				pX.push_back(x);
				pY.push_back(y);
			}
		}
	}
	//建图:每个时间和门的二元组,都确定一个对应的能够从中逃脱的人的集合
	//通过计算这个二元组的和人的匹配数,则可以判断是否所有人都逃脱了。
	// 0 ~ d - 1 : 时间1对应的d个门
	// d ~ 2 * d - 1 : 时间2对应的d个门
	// ……
	// (n - 1) * d ~ n * d - 1 : 时间n-1对应的d个门
	// n * d ~ n * d + p - 1: p个人
	int d = dX.size(), p = pX.size();
	for (int i = 0; i < d; i++) {
		for (int j = 0; j < p; j++) {
			if(dist[dX[i]][dY[i]][pX[j]][pY[j]] >= 0) {
				for (int k = dist[dX[i]][dY[i]][pX[j]][pY[j]]; k <= n; k++) {
					add_edge((k - 1) * d + i, n * d + j);
				}
			}
		}
	}
	
	if (p == 0) {
		printf("0\n");
		return;
	}
	int num = 0;
	memset(match, -1, sizeof(match));
	for (int v = 0; v < n * d; v++) {
		memset(used, 0, sizeof(used));
		if (dfs(v)) {
			if (++num == p) {
				printf("%d\n", v / d + 1);
				return;
			}
		}
	}
	printf("impossible\n");
}

int main() {
	int t;
	cin >> t;
	while(t--) {
		cin >> X >> Y;
		for (int i = 0; i < X; i++) {
			scanf("%s", field[i]);
		}
		solve();
	}
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值