poj 3057 Evacuation 拆点+二分图匹配

由于每时刻每个门都只能有一个人通过。

那么我们就把门拆成N的,分别代表N的时刻。

如果某个人能在A时刻到达这个门,那么从A时刻到最后时刻的这个门都与这个人相连接。

这样来进行二分匹配。

之后从小到大枚举所有的门,累计到达的人数。所有人到达,即可。

#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<algorithm>
using namespace std;
int V,X,Y;
int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
vector<int> G[50010];
vector<int> dx,dy; //门的坐标
vector<int> px,py; //人的坐标
int link[50010];
int dist[13][13][13][13];
bool vis[50010];
char field[13][13];
void addedge(int u,int v){
    G[u].push_back(v);
    G[v].push_back(u);
}
bool dfs(int v){
  vis[v]=true;
  for(int i=0;i<G[v].size();i++){
    int u=G[v][i];
    if(link[u]==-1||!vis[link[u]]&&dfs(link[u])){
        link[v]=u;
        link[u]=v;
        return true;
    }
  }
  return false;
}
void bfs(int x,int y,int d[13][13]){ //传二维数组改变原四位数组
    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+dir[k][0];
            int y2=y+dir[k][1];
            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);
            }//printf("mmmmmmm\n");
        }
    }
}
void solve(){
   int n=X*Y;
   dx.clear();dy.clear();
   px.clear();py.clear();
   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]=='.'){ //if中的==要注意
            px.push_back(x);
            py.push_back(y);
        }
    }
   }
    int d=dx.size(),p=px.size();
    V=X*Y*d+p;
    for(int v=0;v<V;++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++){
                addedge((k-1)*d+i,n*d+j); //该人与不同时间的该门建边
            }
        }
    }
   }
   if(p==0) {
            printf("0\n");
            return;
    }
    int res=0;
    memset(link,-1,sizeof(link));
     for(int v=0;v<n*d;v++){//枚举门的集合
    memset(vis,0,sizeof(vis));
    if(dfs(v)){
        if(++res==p){ //若所有人能逃出
            printf("%d\n",v/d+1);
            return;
        }
    }
   }
   printf("impossible\n");
}
int main()
{
    int i,j,k,t;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&X,&Y);
        for(i=0;i<X;i++) scanf("%s",field[i]);
        solve();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值