POJ 3057 Evacuation 题解

题目

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.

输入格式

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

输出格式

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.

样例输入

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

样例输出

3
21
impossible

题解

这道题要用二分图的最大匹配来做

对于每一扇门,同一秒只能让一个人通过,那我们就为每一扇门在每一秒建一个节点。

对于每一个人,假设他最快可以在 \(t\) 的时间到达门 \(i\),那就把\(t\)到最大时间(图的大小) 这些节点建边连到人 \(j\)

如果在这个图上跑二分匹配,匹配的边 <\(t\)时的门\(i\), 人\(j\)> 就代表人 \(j\) 会在时间 \(t\) 时从门 \(i\) 逃脱。

找最短时间, 不断地枚举点,从该点找增广路。当从时间小的门的点开始枚举,这就代表时间小的门会优先匹配。我们只需看看枚举到哪个时间的门的时候,总匹配数等于人的数量,也是就所有人都被匹配到时,程序就可以结束了。答案就是此时的时间。

实现的时候, 可以从每个门做一次 bfs,来找各个人到各个点所需的时间

代码

#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;
int V, X, Y, dxy[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
char map[13][13];
vector<int> G[50010], dx, dy, px, py;
int match[50005], dist[13][13][13][13];
bool vis[50005];
void add(int u, int v) { G[u].push_back(v), G[v].push_back(u); }
bool find(int v) {
    vis[v] = 1;
    for (int i = 0; i < G[v].size(); i++) {
        int u = G[v][i];
        if (match[u] == -1 || !vis[match[u]] && find(match[u]))
            return match[v] = u, match[u] = v, true;
    }
    return false;
}
void bfs(int x, int y, int d[13][13]) {
    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 k = 0; k < 4; k++) {
            int nx = x + dxy[k][0], ny = y + dxy[k][1];
            if (0 <= nx && nx < X && 0 <= ny && ny < Y && map[nx][ny] == '.' && d[nx][ny] < 0) {
                d[nx][ny] = d[x][y] + 1;
                qx.push(nx), qy.push(ny);
            }
        }
    }
}
int main() {
    int t;
    scanf("%d", &t);
    while (t--) {
        scanf("%d%d", &X, &Y);
        for (int i = 0; i < X; i++) scanf("%s", map[i]);
        int n = X * Y;
        dx.clear(), dy.clear(), px.clear(), py.clear();
        memset(dist, -1, sizeof(dist));
        for (int x = 0; x < X; x++)
            for (int y = 0; y < Y; y++) {
                if (map[x][y] == 'D') {
                    dx.push_back(x), dy.push_back(y), bfs(x, y, dist[x][y]);
                } else if (map[x][y] == '.')
                    px.push_back(x), py.push_back(y);
            }
        int d = dx.size(), p = px.size();
        V = X * Y * d + 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++)
                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((k - 1) * d + i, n * d + j);
        if (p == 0) {
            puts("0");
            continue;
        }
        int res = 0, flag = 1;
        memset(match, -1, sizeof(match));
        for (int v = 0; v < n * d; v++) {
            memset(vis, 0, sizeof(vis));
            if (find(v) && ++res == p) {
                printf("%d\n", v / d + 1);
                flag = 0;
                break;
            }
        }
        if(flag)puts("impossible");
    }
}
posted @ 2020-05-15 22:00  YouXam  阅读(173)  评论(0编辑  收藏  举报