Poj 3057 Evacuation【二分+Bfs建图+二分匹配】好题

142 篇文章 0 订阅
75 篇文章 0 订阅

Evacuation
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 2434 Accepted: 613

Description

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.

Input

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

Output

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. 

Sample Input

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

Sample Output

3
21
impossible

Source


题目大意:

给你一个n*n的一个矩阵,其中”.“代表一个人,”X“代表一个墙,”D“代表门,每秒一个门只能允许一个人通过,问最少时间能够使得所有人通过。


思路:


1、首先我们明确这样一点,我们可以枚举时间,从1开始枚举一直枚举到n+1,尝试使得将所有人都通过门逃生。而且其随着时间枚举的递增,慢慢通过门的人数会增加,其满足递增性,那么我们可以将枚举改成二分查找枚举当前值mid。然后我们讨论当前枚举到的mid值是否可行。


2、对应当前值mid:

①首先将门拆分成mid个点,其分别表示每个时间点只能通过一个人。

②那么我们将从门作为起点Bfs,遇到一个人,并且保证这个人能够在时间范围内通过,假设这个人最快能在T时间到达这个门,那么这个人通过这个门的时间选择可以从T-mid;那么对应建边,从人连到从T-mid这些个门的拆点,对应表示这个人可以选择这些时间通过该门。

③那么对应将每个人看成左集合,将门以及门的拆点看成右集合,对应在建好图之后跑一遍匈牙利算法,得到一个最大匹配值。

④那么很容易判断,如果最大匹配值==总人数,那么就表明所有人可以逃出去,对应当前mid值就是一个可行解。


3、那么根据如上思路,一直二分下去,每得到一个可行解都记录到ans中,直到不能二分为止,输出最终解即可。


Ac代码:

#include<stdio.h>
#include<string.h>
#include<queue>
#include<vector>
using namespace std;
struct node
{
    int x,y,step;
}now,nex;
vector<int >mp[15*15*15*15*2];
int match[15*15*15*15*2];
int vis2[15*15*15*15*2];
char a[200][200];
int tmp[200][200];
int vis[200][200];
int fx[4]={0,0,1,-1};
int fy[4]={1,-1,0,0};
int nn,mm,n,m;
void Bfs_getmap(int ss,int x,int y,int mid)
{
    memset(vis,0,sizeof(vis));
    queue<node>s;
    now.x=x;now.y=y;now.step=0;
    vis[x][y]=1;s.push(now);
    while(!s.empty())
    {
        now=s.front();
        if(a[now.x][now.y]=='.')
        {
            for(int i=now.step;i<=mid;i++)
            {
                //printf("add:%d %d\n",tmp[now.x][now.y],ss+(i-1)*(n-1));
                mp[tmp[now.x][now.y]].push_back(ss+(i-1)*(n-1));
            }
        }
        s.pop();
        for(int i=0;i<4;i++)
        {
            nex.x=now.x+fx[i];
            nex.y=now.y+fy[i];
            nex.step=now.step+1;
            if(nex.step>mid)continue;
            if(nex.x>=1&&nex.x<=nn&&nex.y>=1&&nex.y<=mm&&vis[nex.x][nex.y]==0&&a[nex.x][nex.y]=='.')
            {
                vis[nex.x][nex.y]=1;
                s.push(nex);
            }
        }
    }
}
int find(int u,int mid)
{
    for(int i=0;i<mp[u].size();i++)
    {
        int v=mp[u][i];
        if(vis2[v]==0)
        {
            vis2[v]=1;
            if(match[v]==-1||find(match[v],mid))
            {
                match[v]=u;
                return 1;
            }
        }
    }
    return 0;
}
int Slove(int mid)
{
    for(int i=1;i<=15*15*15*15;i++)mp[i].clear();
    for(int i=1;i<=nn;i++)
    {
        for(int j=1;j<=mm;j++)
        {
            if(a[i][j]=='D')
            {
                Bfs_getmap(tmp[i][j],i,j,mid);
            }
        }
    }
    memset(match,-1,sizeof(match));
    int ans=0;
    for(int i=1;i<=m-1;i++)
    {
        memset(vis2,0,sizeof(vis2));
        if(find(i,mid))ans++;
    }
    if(ans==m-1)return 1;
    else return 0;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        memset(tmp,0,sizeof(tmp));
        scanf("%d%d",&nn,&mm);
        for(int i=1;i<=nn*mm;i++)mp[i].clear();
        for(int i=1;i<=nn;i++)
        {
            scanf("%s",a[i]+1);
        }
        n=1;
        m=1;
        for(int i=1;i<=nn;i++)
        {
            for(int j=1;j<=mm;j++)
            {
                if(a[i][j]=='.')
                {
                    tmp[i][j]=m;
                    m++;
                }
                if(a[i][j]=='D')
                {
                    tmp[i][j]=n;
                    n++;
                }
            }
        }
        int ans=-1;
        int mid;
        int l=0;
        int r=120;
        while(r>=l)
        {
            mid=(l+r)/2;
            if(Slove(mid)==1)
            {
                r=mid-1;
                ans=mid;
            }
            else l=mid+1;
        }
        if(ans!=-1)
        printf("%d\n",ans);
        else printf("impossible\n");
    }
}



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值