有一个 N * M 的矩形区域,每个格子如果是 ‘ . ’ 则此处有一个人,如果是 ‘ D ’ 则这里是门(门只出现在边界上),否则这里就是墙。每个人可以在单位 1 的时间走到与其相邻的上下左右任意一个格子里,也可以不动。每个格子可以站无数个人,但是每扇门每一个单位时间只能出去一个人,问所有人离开这个矩形区域所需最短时间,如果不行,输出 ‘ impossible ' 。
二分时间,再用网络流判断:S 向每个人连一条 1 的边,每个人向在二分所得时间内能到达的门连 1 的边,每扇门向 T 连容量为二分的时间的边。看最后的最大流是不是等于总人数。看起来很正确,但是我们忽略了题目中的一个条件,即 “每扇门每一个单位时间只能出去一个人”。比如这种情况:
XXDX
X . . X
X . . X
XXXX
正确答案是 4,而我们所求的答案是 3 ,因为最后两个人是同时从门里出来的。怎么办呢?我们把每个门按时间拆点:S 向每个人连一条 1 的边,每个人向二分时间能到达的时间连边 1,即设二分时间为 X,第 i 个人到达 j 号门的时间为 Y, 且 Y <= X,既然 i 在 Y时间就能到门口,那么 i 也没有必要立刻就出去,在 Y ~ X 这段时间他都可以出去,所以缓一缓,就能避免同时出去的情况,最后每个时间的门向 T 连 1 的边,最大流判断即可。
#include
#include
#include
using namespace std;
const int MAX_N = 25000, MAX_M = 2005000;
const int inf = 1000000007;
struct node {
int v, w, next;
}E[MAX_M << 1];
int head[MAX_N], cur[MAX_N], top = 0;
int n, m, tot = 0, S, T, rx[405], ry[405], fx[405], fy[405], dor = 0, mp[25][25];
int dep[MAX_N], q[MAX_M], dis[405][25][25], s[MAX_M], xx[MAX_M], yy[MAX_M];
char str[25];
int dx[4] = {-1, 0, 0, 1}, dy[4] = {0, -1, 1, 0};
inline void add(int u, int v, int w, int x) {
int t1 = x << 1, t2 = t1 + 1;
E[t1].v = v; E[t1].w = w; E[t1].next = head[u]; head[u] = t1;
E[t2].v = u; E[t2].w = 0; E[t2].next = head[v]; head[v] = t2;
}
inline bool bfs1(int k, int x, int y) {
for (int i = 1; i <= n; i ++)
for (int j = 1; j <= m; j ++) dis[k][i][j] = inf;
int bg = 0, ed = 1;
s[bg] = 0; xx[bg] = x; yy[bg] = y;
while (bg < ed) {
for (int i = 0; i < 4; i ++) {
int nx = xx[bg] + dx[i], ny = yy[bg] + dy[i];
if (nx < 1 || nx > n || ny < 1 || ny > m || !mp[nx][ny]) continue;
if (dis[k][nx][ny] == inf) {
dis[k][nx][ny] = s[bg] + 1;
if (mp[nx][ny] == 2) continue;
s[ed] = s[bg] + 1;
xx[ed] = nx; yy[ed] = ny; ed ++;
}
}
bg ++;
}
}
inline bool bfs() {
memset(dep, -1, sizeof(dep));
int bg = 0, ed = 1;
q[ed] = S; dep[S] = 0;
while (bg < ed) {
int x = q[++ bg];
for (int i = head[x]; i != -1; i = E[i].next) {
if (dep[E[i].v] == -1 && E[i].w) {
dep[E[i].v] = dep[x] + 1; q[++ ed] = E[i].v;
if (E[i].v == T) return 1;
}
}
}
return 0;
}
int dfs(int x, int mx) {
if (x == T) return mx;
int ret;
for (int &i = cur[x]; i != -1; i = E[i].next)
if (E[i].w && dep[E[i].v] == dep[x] + 1 && (ret = dfs(E[i].v, min(mx, E[i].w)))) {
E[i].w -= ret; E[i ^ 1].w += ret;
return ret;
}
return 0;
}
inline int dinic() {
int ans = 0, ret;
while (bfs()) {
for (int i = 0; i <= T; i ++) cur[i] = head[i];
while (ret = dfs(S, inf)) ans += ret;
}
return ans;
}
inline bool check(int x) {
S = 0;
memset(head, -1, sizeof(head)); top = 0;
for (int i = 1; i <= tot; i ++) add(S, i, 1, top ++);
for (int i = 1; i <= tot; i ++)
for (int j = 1; j <= dor; j ++)
if (dis[i][fx[j]][fy[j]] <= x){
for (int k = dis[i][fx[j]][fy[j]]; k <= x; k ++){
int k1 = i, k2 = k * dor - dor + j + tot;
add(k1, k2, 1, top ++);
}
}
T = tot + x * dor + 1;
for (int i = 1; i <= x * dor; i ++) add(tot + i, T, 1, top ++);
if (dinic() == tot) return 1;
return 0;
}
void init() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i ++) {
scanf("%s", str + 1);
for (int j = 1; j <= m; j ++) {
if (str[j] == '.') rx[++ tot] = i, ry[tot] = j, mp[i][j] = 1;
else if (str[j] == 'D') fx[++ dor] = i, fy[dor] = j, mp[i][j] = 2;
else mp[i][j] = 0;
}
}
for (int i = 1; i <= tot; i ++)
bfs1(i, rx[i], ry[i]);
}
void doit() {
int l = 0, r = 400, mid;
while (l <= r) {
mid = (l + r) >> 1;
if (check(mid)) r = mid - 1;
else l = mid + 1;
}
if (l >= 400) printf("impossible\n");
else printf("%d\n", l);
}
int main() {
init();
doit();
return 0;
}