【最大匹配+二分答案】POJ 3057 Evacuation

题目大意

POJ链接
有一个\(X×Y\)的房间,X代表墙壁,D是门,.代表人。这个房间着火了,人要跑出去,但是每一个时间点只有一个人可以从门出去。
问最后一个人逃出去的最短时间,如果不能逃出去,输出impossible

输入格式

第一行一个整数\(T\),表示有T组数据。
每组数据,第一行两个数字\(Y,X\),接下来有一个\(X×Y\)的图。

输出格式

\(T\)行答案,表示最后一个人逃出去的最短时间,如果不能逃出去,输出impossible

数据范围

\(3\le Y\le X\le 12\)

样例输入

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\),那么可以把从\(t\)到最大时间每一秒的这扇门和这个人建边,因为只要能到达,之后任何一秒都可以选择出去。从时间小的门的点开始匹配。看一下多长时间人和们都能匹配上。
查询人到门的距离用BFS,而最后求答案要用二分。

代码

#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;
const int maxn=1e6+10;
const int dx[4]={0,0,1,-1};
const int dy[4]={1,-1,0,0};
int x,y;
char g[20][20];
int match[maxn];

struct Edge{
    int to,nxt,val;
}edge[maxn];

struct Node{
    int x,y,t;
    Node(){}
    Node(int a,int b,int c){
        x=a;y=b;t=c;
    }
};

int head[maxn],tot;
void add(int a,int b,int w){
    edge[++tot].to=b;
    edge[tot].val=w;
    edge[tot].nxt=head[a];
    head[a]=tot;
}

queue<Node> q;
int cal1(int a,int b){
    return y*(a-1)+b;//两个数映射成一个数
}

int cal2(int a,int b,int t){
    return (cal1(a,b)-1)*100+t;
}

void add2(int a,int b,int xx,int yy,int t){
    for(int i=t;i<=100;i++)
        add(cal1(a,b),cal2(xx,yy,i),i);//把这个人和之后分成的多个门建边
}

bool vis[20][20];
void bfs(int a,int b){
    memset(vis,0,sizeof(vis));
    vis[a][b]=true;
    q.push(Node(a,b,0));
    while(!q.empty()){
        Node now=q.front();q.pop();

        for(int i=0;i<4;i++){
            int xx=now.x+dx[i];
            int yy=now.y+dy[i];

            if(xx>=1&&xx<=x&&yy>=1&&yy<=y&&!vis[xx][yy]&&g[xx][yy]=='.'){
                vis[xx][yy]=true;
                q.push(Node(xx,yy,now.t+1));
            }
            
            if(xx>=1&&xx<=x&&yy>=1&&yy<=y&&!vis[xx][yy]&g[xx][yy]=='D'){
                vis[xx][yy]=true;
                add2(a,b,xx,yy,now.t+1);//找到了门
            }
        }
    }
}

bool vis2[maxn];
bool dfs(int x,int t){
    for(int i=head[x];i;i=edge[i].nxt){
        if(vis2[edge[i].to]||edge[i].val>t)continue;
        vis2[edge[i].to]=true;
        if(match[edge[i].to]==0||dfs(match[edge[i].to],t)){
            match[edge[i].to]=x;
            return true;
        }
    }
    return false;
}

bool judge(int t){
    memset(match,0,sizeof(match));
    for(int i=1;i<=x;i++)
        for(int j=1;j<=y;j++)
            if(g[i][j]=='.'){
                memset(vis2,0,sizeof(vis2));
                if(!dfs(cal1(i,j),t))return false;
            }
    return true;
}

void arrclear(){
    memset(head,0,sizeof(head));

}

int main(){
    freopen("123.txt","r",stdin);

    int T;
    scanf("%d",&T);
    while(T--){
        arrclear();
        scanf("%d%d",&x,&y);

        tot=0;
        for(int i=1;i<=x;i++)
            for(int j=1;j<=y;j++)
                scanf(" %c",&g[i][j]);

        for(int i=1;i<=x;i++)
            for(int j=1;j<=y;j++)
                if(g[i][j]=='.')bfs(i,j);//找到人bfs

        int l=0,r=100;//卡的贼严,还是开小点
        while(l<=r){//二分答案
            int mid=(l+r)/2;
            if(judge(mid))r=mid-1;
            else l=mid+1;
        }
        if(l==100+1)printf("impossible\n");
        else printf("%d\n",l);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Midoria7/p/12901486.html