POJ 3057 Evacuation (二分图匹配)

题目链接:http://poj.org/problem?id=3057

 

题意:在一个X * Y的区域内,每个点可能是墙壁,门,空区域的人,现在房间起火了,每个门每秒只允许一个人通过,每个人每秒只能移动一步,求最后一个人逃脱的最短时间,如果有人无法逃脱,输出"impossible"

思路:由于每秒只能通过一个人,所以不能广搜最大值作为答案,考虑一个门,能在时间t内逃脱的人,是在距离门t以内的人,并且只有一人能够从该门逃脱,每个时间和门的二元组,都确定一个对应的能够从中逃脱的人的几何,通过计算这个二元组和人的二分图的匹配数,即可判断所有人能否可以逃脱。如果按照二分来搜索结果,会有重复计算,考虑如果在已知的T时间无法逃脱,检查T1 > T 时,比较之前的图,只是增加了其中一侧的顶点和与之对应的边,而在二分图最大匹配算法中,它是按顺序从一侧的顶点开始寻找增广路增广。因此,要求T1对应的最大匹配,只要在已求得的最大匹配基础上,继续从新增加的顶点开始寻找增广路即可,故更有效的解法是直接每次将T递增1然后寻求对应的二分图最大匹配

 

挑战程序上的代码:

 

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#include <vector>

using namespace std;

const int maxn = 15;
const int maxd = 10010;

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

char field[maxn][maxn];
vector <int> dX, dY;
vector <int> pX, pY;

int dist[maxn][maxn][maxn][maxn];
vector <int> G[maxd];
int match[maxd];
bool used[maxd];

void add_adge(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[v] = u;
            match[u] = v;
            return true;
        }
    }
    return false;
}

void bfs(int x, int y, int d[maxn][maxn])
{
    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 x2 = x + dx[k], y2 = y + dy[k];
            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()
{
    dX.clear();
    dY.clear();
    pX.clear();
    pY.clear();
    int n = (X - 1) * (Y - 1);
    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);
                }
        }
    int d = dX.size(), p = pX.size();
    for(int v = 0; v <  n * d; 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_adge((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;
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d %d", &X, &Y);
        for(int i = 0; i <  X; i++)
            scanf("%s", field[i]);
        solve();
    }
    return 0;
}

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值