POJ 3057:Evacuation (搜索+二分图匹配)

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


题目翻译:

有一个X*Y的房间,每块区域只能是墙 ‘X’  , 空地'.' 或门 ’D‘。最外层

的区域一定是门或者墙壁,而内部区域一定没有门,假设房间发生火情,

每一个空地都站着一个人,而外侧的每个门每一秒只能出一个人,每

个空地每一秒也只能站一个人,一个人其逃跑路线只能是向上、向下、

向左、向右,问所有人都能顺利逃脱的最小时间,如果不能使所有人都

顺利逃脱输出impossible


题目思路:

来源于挑战程序设计竞赛。

其思路是搜索+二分图匹配。 相当难的一个题目,搜索还能想到,但是还

涉及到二分图匹配,真的想不到。

由于门每秒中只能出一个人,假如有多个人都能够在T秒内到达某个门,对于

这个门来说只能让一个人出,则其他几个人必然还要在等下一秒来看看有没有

可能逃出去。所以不能简单去广搜,已某个人距某个门的最大时间作为答案,

因为存在着等待过程。首先我们从每个门开始对人进行广搜,搜索任意一个人

到任意一个门的最短时间。

则我们得到如下想法:

某个人P到某个门D的最短时间是T,那么它可能在T秒或、T+1秒、或T+2.....从

这个门逃走。则二分图的集合分为两个,门组成的集合和人组成的集合。则人,门

合适该人从该门逃走,共同组成了一个状态。

因此我们对门采取下面编号:

0~d-1    :是第一秒内的门的编号。

d~2*d-1 :是第二秒内的门的编号。

2*d ~ 3*d-1:是第三秒内的门的编号。

.............. 

(注意虽然门是相同的门,但是其时间不同,我们视其状态不同)

那么时间的上限应该以多久为上限呢?最大的时间我们设为矩阵面积,因为在

这个时间内如果某个人不是被墙围死了,或者区域根本就没有门,那么在这个

时间内所有人肯定都逃除去完了。

因此门的编号就到  (X*Y-1)*d ~ (X*Y)*d - 1:

而人的编号顺着这个编号编下去即可。

某个人P到某个门D的最短时间是T,那么它可能在T秒或、T+1秒、或T+2.....从该门

逃走,则P应该在T秒与D建立关系,T+1秒与D建立关系,T+2秒与D建立关系,

这些关系对应二分图中的一条边。


然后进行二分图匹配,匹配数目等于人数的时候算法结束。



AC代码:

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <queue>
#include <vector>

using namespace std;

const int maxn = 50;
int dir[4][2] = {{-1,0},{0,1},{1,0},{0,-1}};
char Map[maxn][maxn];
int X,Y;
int dist[maxn][maxn][maxn][maxn];
int match[10000],mark[10000];
vector<int>DX,DY;
vector<int>PX,PY;
vector<int>g[10000];
queue<int>qu1;
queue<int>qu2;

void bfs(int x,int y,int d[maxn][maxn])
{
    qu1.push(x);    ///起点横坐标进队
    qu2.push(y);    ///起点纵坐标进队
    d[x][y] = 0;    ///开始步数为0
    int x1,y1,x2,y2;
    while(!qu1.empty())
    {
        x1 = qu1.front();
        qu1.pop();
        y1 = qu2.front();
        qu2.pop();
        ///4个方向进行广搜
        for(int i = 0; i < 4; i++)
        {
            x2 = x1 + dir[i][0];
            y2 = y1 + dir[i][1];
            if(x2>=0&&x2<X&&y2>=0&&y2<Y&&Map[x2][y2]=='.'&&d[x2][y2]<0)
            {
                d[x2][y2] = d[x1][y1] + 1;
                qu1.push(x2);
                qu2.push(y2);
            }
        }
    }
}
bool hungry(int u)
{
    mark[u] = 1;
    for(int  i = 0; i < (int)g[u].size(); i++)
    {
        int v = g[u][i];
        int w = match[v];
        if(w < 0 || (mark[w]==0 && hungry(w)))
        {
            match[u] = v;
            match[v] = u;
            return true;
        }
    }
    return false;
}
void allSurvive()
{
    int d = DX.size();   ///d是门的数量
    int p = PX.size();   ///p是人的数量
    /**最大的时限是矩阵的面积*/
    int n = X*Y;
    /**0~d-1  :时间1对应的门
    d~2*d-1   : 时间2对应的门
    2*d~3*d-1 : 时间3内对应的门
    .......
    (n-1)*d ~ n*d-1:时间n内对应的门
    n*d ~ n*d+p-1 : 对应人的编号
    */
    int V = n*d + p;
    for(int i = 0; i < V; i++)
        g[i].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++)
                {
                    int u = (k-1)*d + i;
                    int v = n*d + j;
                    g[u].push_back(v);
                    g[v].push_back(u);
                }
            }
        }
    }
    memset(match,-1,sizeof(match));
    int ccount = 0;
    for(int i = 0; i < n*d; i++)
    {
       memset(mark,0,sizeof(mark));
       if(hungry(i))
       {
           ccount++;
           if(ccount == (int)PX.size())
           {
               printf("%d\n",i/d+1);
               return;
           }
       }
    }
    printf("impossible\n");
    return;
}
int main()
{
    int T;
    scanf("%d",&T);   ///T组测试数据
    while(T--)
    {
        DX.clear();   ///存放门的横坐标,首先清空
        DY.clear();   ///存放门的纵坐标,首先清空
        PX.clear();   ///存放人的横坐标,首先清空
        PY.clear();   ///存放人的纵坐标,首先清空
        scanf("%d%d",&X,&Y);  ///输入矩阵规模
        for(int i = 0; i < X; i++)
        {
            for(int j = 0; j < Y; j++)
            {
                scanf(" %c",&Map[i][j]); ///注意吞掉空格
                if(Map[i][j] == 'D')
                {
                    DX.push_back(i);
                    DY.push_back(j);
                }
                else if(Map[i][j] == '.')
                {
                    PX.push_back(i);
                    PY.push_back(j);
                }
            }
        }
        if((int)PX.size() == 0)     ///人的人数为0.
            printf("0\n");
        else
        {
            memset(dist,-1,sizeof(dist));
            for(int i = 0; i < (int)DX.size(); i++)
            {
                bfs(DX[i],DY[i],dist[DX[i]][DY[i]]);  ///以门为起点进行广搜
            }
            allSurvive(); ///调用函数求所有人都逃生成功的时间
        }
    }
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值