POJ 3057 Evacuation 二分图

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

题意:一个二维平面图,有“D”门,“X”墙,“.”人,每个门每秒只能走一个人,人可以重叠,问最短几秒人可以走光。

思路:开始胡乱YY的,就没写,后来看的题解= =(弱逼不会建图啊啊啊啊啊)。二分图最大匹配要求一个点只能连一条边,这个题的门显然不符合。不过可以把门拆成第x秒的门,这样就符合了,二分答案,建图。


具体做法:

1.把相邻的能走的点之间的距离设为1

2.floyd求出任意两点之间距离

3.把人和门分别存到两个vector里,vector里面存的是在数组中的位置

4.二分答案

5.建图:枚举人和门,如果距离小于当前二分的答案,就建边(可能不止建一条边,我可以停顿1s在过去||在门口排队。。)


代码://115行之后开始=。=

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <stack>
using namespace std;
#define mem(a,b) memset((a),(b),sizeof((a)))
#define For(i,a,b) for(int (i)=(a);(i) < (b);(i)++)
#define Ror(i,a,b) for(int (i)=(a);(i) > (b);(i)--)
#define mp make_pair
#define pb push_back
#define inf 0x3f3f3f3f

void RI (int& x){
    x = 0;
    char c = getchar ();
    while (c == ' '||c == '\n')    c = getchar ();
    bool flag = 1;
    if (c == '-'){
        flag = 0;
        c = getchar ();
    }
    while (c >= '0' && c <= '9'){
        x = x * 10 + c - '0';
        c = getchar ();
    }
    if (!flag)    x = -x;
}
void RII (int& x, int& y){RI (x), RI (y);}
void RIII (int& x, int& y, int& z){RI (x), RI (y), RI (z);}

const int maxn = 30000;
const int maxm = 40001000;

vector<int> g[maxn];
int Mx[maxn],My[maxn],Nx,Ny;
int dx[maxn],dy[maxn],dis;
bool vst[maxn];
bool searchP()
{
    queue<int>Q;
    dis=inf;
    memset(dx,-1,sizeof(dx));
    memset(dy,-1,sizeof(dy));
    for(int i=0;i<Nx;i++)
        if(Mx[i]==-1)
        {
            Q.push(i);
            dx[i]=0;
        }
    while(!Q.empty())
    {
        int u=Q.front();
        Q.pop();
        if(dx[u]>dis)  break;
        int siz = g[u].size();
        For(i,0,siz){//cout<<siz;
            int v = g[u][i];
            if(dy[v]==-1)
            {
                dy[v]=dx[u]+1;
                if(My[v]==-1)  dis=dy[v];
                else
                {
                    dx[My[v]]=dy[v]+1;
                    Q.push(My[v]);
                }
            }
        }//
    }
    return dis!=inf;
}
bool DFS(int u)
{
    int siz = g[u].size();
    For(i,0,siz){
        int v = g[u][i];
       if(!vst[v]&&dy[v]==dx[u]+1)
       {
           vst[v]=1;
           if(My[v]!=-1&&dy[v]==dis) continue;
           if(My[v]==-1||DFS(My[v]))
           {
               My[v]=u;
               Mx[u]=v;
               return 1;
           }
       }
   }
    return 0;
}
int MaxMatch()
{
    int res=0;
    memset(Mx,-1,sizeof(Mx));
    memset(My,-1,sizeof(My));
    while(searchP())
    {
        memset(vst,0,sizeof(vst));
        for(int i=0;i<Nx;i++)
          if(Mx[i]==-1&&DFS(i))  res++;
    }
    //For(i,0,Nx)if(Mx[i] == -1)cout<<i<<endl;
    return res;
}
//**************************************************************************/
char mm[400][400];
int ddd[400][400];
int ddx[4] = {0,0,1,-1};
int ddy[4] = {1,-1,0,0};
int cc[400] = {1,-1,};
vector<int>door,peo;
void build(int mid){
    Nx = door.size()*mid;
    Ny = peo.size();
    For(i,0,Nx)g[i].clear();
    For(i,0,Nx/mid){
        For(j,0,Ny){
            For(k,ddd[door[i]][peo[j]],mid+1){
                g[i*mid+k-1].pb(j);
            }
        }
    }
}
int get(int r){
    int l = 1,mid;
    while(l < r){
        mid = (l + r) >> 1;
        build(mid);
        int ans = MaxMatch();//if(mid == 3)cout<<ans<<endl;
        if(ans < peo.size())l = mid + 1;
        else r = mid;
    }
    return r;
}
bool vis[400];
bool judge(){
    mem(vis,0);
    int siz = door.size();
    int peo_num = peo.size();//cout<<siz<<' '<<peo_num;
    For(i,0,siz){
        int now = door[i];
        For(j,0,peo_num){
            if(ddd[now][peo[j]] != inf)vis[peo[j]] = 1;
        }
    }
    For(i,0,peo_num){
        if(vis[peo[i]] == 0)return false;
    }
    return true;
}
int main(){
    //freopen("test.txt","r",stdin);
    int T;
    RI(T);
    while(T --){
        int n,m;
        RII(n,m);
        For(i,0,n){
            scanf("%s",mm[i]);
        }
        mem(ddd,inf);
        door.clear();
        peo.clear();
        cc[2] = m;cc[3] = -m;
        For(i,0,n*m){
            int x = i/m,y = i%m;
            if(mm[x][y] == 'X')continue;
            if(mm[x][y] == 'D')door.pb(i);
            else peo.pb(i);
            For(j,0,4){
                int tx = x + ddx[j],ty = y + ddy[j];
                if(tx >= 0&&ty >= 0&&tx < n&&ty < m&&
                  ((mm[x][y] == '.'&&mm[tx][ty] == '.')||
                   (mm[x][y] == '.'&&mm[tx][ty] == 'D')||
                   (mm[x][y] == 'D'&&mm[tx][ty] == '.')))
                {
                    ddd[i][i+cc[j]] = 1;//cout<<x<<' '<<y<<' '<<tx<<' '<<ty<<endl;
                }
            }
        }
        For(i,0,n*m)ddd[i][i] = 0;
        For(k,0,n*m){
            For(i,0,n*m){
                For(j,0,n*m){
                    ddd[i][j] = min(ddd[i][j],ddd[i][k] + ddd[k][j]);
                }
            }
        }
        if(judge() == false){
            puts("impossible");
            continue;
        }
        else {//cout<<peo.size();
            int ans = get(n*m);
            if(peo.size() == 0)ans = 0;
            cout<<ans<<endl;
        }
    }
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值