题意
传送门 POJ 3057
题解
基本按照白书思路写的。考虑到不同时间不同的门只有一个人可以逃生,则每个时间和门的二元组,都确定一个对应的能逃生的人的集合。一个人否在某个时间点从某个门逃生,可以根据 bfs 求得的最短路确定 。每一个二元组作为一个顶点向在该时间点能逃生的人的集合连边,按时间顺序新增二元组顶点寻找增广路增广即可,满足匹配数等于人数的最小时间即答案。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <queue>
#include <vector>
#define min(a,b) (((a) < (b)) ? (a) : (b))
#define max(a,b) (((a) > (b)) ? (a) : (b))
#define abs(x) ((x) < 0 ? -(x) : (x))
#define INF 0x3f3f3f3f
#define delta 0.85
#define eps 1e-5
#define PI 3.14159265358979323846
#define MAX_X 12
#define MAX_Y 12
using namespace std;
const int dx[4] = {0, 0, 1, -1};
const int dy[4] = {1, -1, 0, 0};
int X, Y;
char field[MAX_X][MAX_Y + 1];
int dist[MAX_X][MAX_Y][MAX_X][MAX_Y];
vector<int> pX, pY, dX, dY;
void bfs(int x, int y, int d[MAX_X][MAX_Y]){
d[x][y] = 0;
queue<int> qx, qy;
qx.push(x);
qy.push(y);
while(!qx.empty()){
int x = qx.front(); qx.pop();
int y = qy.front(); qy.pop();
for(int i = 0; i < 4; i++){
int nx = x + dx[i], ny = y + dy[i];
if(nx >= 0 && nx < X && ny >= 0 && ny < Y && field[nx][ny] == '.' && d[nx][ny] == INF){
d[nx][ny] = d[x][y] + 1;
qx.push(nx);
qy.push(ny);
}
}
}
}
const int MAX_V = MAX_X * MAX_Y * 2 * (MAX_X + MAX_Y) + MAX_X * MAX_Y;
int V;
vector<int> G[MAX_V];
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] = 1;
for(int i = 0; i < G[v].size(); i++){
int u = G[v][i], w = match[u];
if(w == -1 || (!used[w] && dfs(w))){
match[u] = v;
match[v] = u;
return true;
}
}
return false;
}
int main(){
int t;
scanf("%d", &t);
while(t--){
pX.clear(); pY.clear();
dX.clear(); dY.clear();
memset(dist, 0x3f, sizeof(dist));
scanf("%d%d", &X, &Y);
for(int i = 0; i < X; i++) scanf(" %s", field + i);
// 计算人到各个门的最短距离
for(int i = 0; i < X; i++){
for(int j = 0; j < Y; j++){
if(field[i][j] == 'D'){
bfs(i, j, dist[i][j]);
dX.push_back(i);
dY.push_back(j);
}
else if(field[i][j] == '.'){
pX.push_back(i);
pY.push_back(j);
}
}
}
// 门 i 在时间 k 的索引 : (k - 1) * d + i
// 人 j 的索引: n * d + j
int p = pX.size(), d = dX.size(), n = X * Y, V = d * n + p;
for(int v = 0; v < V; v++) G[v].clear();
for(int i = 0; i < d; i++){
for(int j = 0; j < p; j++){
int t;
if((t = dist[dX[i]][dY[i]][pX[j]][pY[j]]) != INF){
for(int k = t ; k <= n; k++){
add_edge((k - 1) * d + i, n * d + j);
}
}
}
}
// 按照时间递增顺序增广
bool flag = 0;
int cnt = 0, sz = n * d;
memset(match, -1, sizeof(match));
for(int v = 0; v < sz; v++){
memset(used, 0, sizeof(used));
if(dfs(v)){
++cnt;
if(cnt == p){
flag = 1;
printf("%d\n", v / d + 1);
break;
}
}
}
if(!flag) printf("impossible\n");
}
return 0;
}