POJ3057_Evacuation_二分图匹配::逃生问题

来源:互联网 发布:淘宝开店教程下载 编辑:程序博客网 时间:2024/04/30 21:05

题意

给出一张地图, “D” 表示门,”.” 表示人, “X” 表示墙。人不会出现在最外面一圈,门只会出现在最外面一圈,并且一扇门一秒钟只能有一人通过。现在起火了,求所有通过门逃出地图的最短时间。如果有人逃不出去就输出 impossible。

思路

看到这个题首先想到的是最小费用流。但是,“一秒钟只能通过一个人”这个限制条件无法克服。这个问题本质上是人和门的匹配问题,因为门多了一个时间属性,所以可以用拆点法归结成一个二分图匹配的问题。

1.把每个人看成一个点,构成集合 P;把每一时刻的门看成一个点,构成集合 D。因为最大的可能时间是地图的网格总数(设想为只有一扇门,其他格都站满了人的情况),所以集合 D 的规模在可接受的范围内。
2.从每一扇门出发进行bfs,求出每个人到这扇门的最短距离。
3.对于每一扇门,利用 2 中求出来的最短时间,把能到达它的人与对应时刻的它之间连一条边。
4.依次对 D 集合中的每一个点进行匹配,一旦匹配数达到 P, 则这个点对应的时间就是需要花费的最小时间。
5.如果时间达到了上界,但匹配数依然小于 P,输出 impossible 。

需要注意的一点是,这种做法需要特判 P = 0 的情况,否则在这种情况下会输出 impossible 。

题目链接

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

AC代码

#include<cstdio>#include<iostream>#include<cstring>#include<vector>#include<queue>using namespace std;const int maxn = 13;const int mx[4] = {0, 0, -1, 1};const int my[4] = {1, -1, 0, 0};int cas, row, col;char Map[maxn][maxn];int dis[maxn][maxn][maxn][maxn];//门到人的距离:这么存很暴力但是比较方便vector<int> G[maxn * maxn * maxn * 5];int match[maxn * maxn * maxn * 5];bool usd[maxn * maxn * maxn * 5];vector<int> dx, dy;//存门的坐标vector<int> px, py;//存人的坐标//bfs 求出门到各个人的最短距离void bfs(int x, int y, int dis[maxn][maxn]){    dis[x][y] = 0;    queue<int> qx, qy;    qx.push(x);qy.push(y);    while(qx.size())    {        x = qx.front();qx.pop();        y = qy.front();qy.pop();        for(int i= 0; i< 4; i++)        {            int cx = x + mx[i], cy = y + my[i];            if(cx >= 0 && cx < row && cy >= 0 && cy < col                && Map[cx][cy] == '.' && dis[cx][cy] < 0)                {                    dis[cx][cy] = dis[x][y] + 1;                    qx.push(cx);                    qy.push(cy);                }        }    }}void add(int a, int b){    G[a].push_back(b);    G[b].push_back(a);}bool dfs(int v){    usd[v] = true;    for(int i= 0; i< G[v].size(); i++)    {        int u = G[v][i], w = match[u];        if(w < 0 || !usd[w] && dfs(w))        {            match[u] = v;            match[v] = u;            return true;        }    }    return false;}int main(){    cin >> cas;    while(cas --)    {        memset(match, -1, sizeof match);        memset(dis, -1, sizeof dis);        dx.clear(); dy.clear();        px.clear(); py.clear();        for(int i= 0; i< maxn * maxn * maxn * 5; i++)            G[i].clear();        cin >> row >> col;        int m = row * col;        for(int i= 0; i< row; i++)            for(int j= 0; j< col; j++)            cin >> Map[i][j];        for(int i= 0; i< row; i++)            for(int j= 0; j< col; j++)        {            if(Map[i][j] == 'D')            {                dx.push_back(i);                dy.push_back(j);                bfs(i, j, dis[i][j]);            }            else if(Map[i][j] == '.')            {                px.push_back(i);                py.push_back(j);            }        }        int D = dx.size(), P = px.size();        //重要特判        if(P == 0)        {            cout << "0\n";            continue;        }        for(int i= 0; i< D; i++)            for(int j= 0; j< P; j++)        {            //人 和 时刻门 之间连边            if(dis[dx[i]][dy[i]][px[j]][py[j]] > 0)                for(int t= dis[dx[i]][dy[i]][px[j]][py[j]]; t<= m; t++)                    add((t - 1) * D + i, m * D + j);        }        int sum = 0;        //枚举匹配 时刻门        for(int i= 0; i< m * D; i++)        {            memset(usd, false, sizeof usd);            if(dfs(i))            {                sum ++;                if(sum == P)                {                    cout << i / D + 1 << endl;                    break;                }            }        }        if(sum < P)            cout << "impossible\n";    }    return 0;}
原创粉丝点击