zoukankan      html  css  js  c++  java
  • bzoj千题计划132:bzoj1189: [HNOI2007]紧急疏散evacuate

    http://www.lydsy.com/JudgeOnline/problem.php?id=1189

    二分答案

    源点向人连边,流量为1

    门拆为mid个点,同一个门的第j个点向第j+1个点连边,流量为inf

    若第i个人第k秒到达第j个门,第i个人向第j个门拆出的第k个点连边,流量为1

    所有门向汇点连边,流量为1

    用ISAP写的,真快,也真TM长

    #include<queue>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
     
    using namespace std;
     
    #define N 36001
    #define M 320001
     
    const int inf=500;
     
    int mp[21][21];
     
    int n,m,cnt;
    char s[21];
     
    int sum[401],tim[N][81],wh[N][81];
     
    int sum_door,doorx[81],doory[81],id_door[21][21];
     
    int dis[21][21];
    int dx[4]={-1,0,1,0};
    int dy[4]={0,1,0,-1};
     
    int tot;
    int front[N],nxt[M<<1],to[M<<1],val[M<<1],from[M<<1];
    int lev[N],num[N];
    int path[N];
    int cur[N];
     
    int src,decc;
     
    struct node
    {
        int x,y;
    }nw,nt;
     
    bool inmap(int x,int y)
    {
        if(x<=0 || x>n || y<=0 || y>m) return false;
        return mp[x][y];
    }
     
    void prebfs(int x,int y)
    {
        queue<node>q;
        nw.x=x; nw.y=y;
        q.push(nw);
        while(!q.empty())
        {
            nw=q.front();
            q.pop();
            for(int i=0;i<4;++i)
            {
                nt.x=nw.x+dx[i];
                nt.y=nw.y+dy[i];
                if(!inmap(nt.x,nt.y)) continue;
                if(!dis[nt.x][nt.y]) 
                {
                    dis[nt.x][nt.y]=dis[nw.x][nw.y]+1;
                    if(mp[nt.x][nt.y]==2) continue;
                    q.push(nt);
                }
            }
        }
    }
     
    void add(int u,int v,int w)
    {
        to[++tot]=v; nxt[tot]=front[u]; front[u]=tot; from[tot]=u; val[tot]=w;
        to[++tot]=u; nxt[tot]=front[v]; front[v]=tot; from[tot]=v; val[tot]=0;
    }
     
    void rebuild(int mid)
    {
        tot=1;
        memset(front,0,sizeof(front));
        decc=mid*sum_door+cnt+1;
        for(int i=1;i<=cnt;++i) add(src,i,1);
        for(int i=1;i<=sum_door;++i)
            for(int j=1;j<=mid;++j)
            {
                add((i-1)*mid+j+cnt,decc,1);
                if(j!=mid) add((i-1)*mid+j+cnt,(i-1)*mid+j+cnt+1,inf);
            }
        for(int i=1;i<=cnt;++i)
            for(int j=1;j<=sum[i];++j)
                if(tim[i][j]<=mid) add(i,(wh[i][j]-1)*mid+tim[i][j]+cnt,1);
    }
     
    bool bfs()
    {
        queue<int>q;
        for(int i=src;i<=decc;++i) lev[i]=decc;
        q.push(decc);
        lev[decc]=0;
        int now,t;
        while(!q.empty())
        {
            now=q.front();
            q.pop();
            for(int i=front[now];i;i=nxt[i])
            {
                t=to[i];
                if(lev[t]==decc && val[i^1]) 
                {
                    lev[t]=lev[now]+1;
                    q.push(t);
                }
            }
        }
        return lev[src]!=decc;
    }
     
    int augment()
    {
        int now=decc,flow=inf;
        int i;
        while(now!=src)
        {
            i=path[now];
            flow=min(flow,val[i]);
            now=from[i];
        }
        now=decc;
        while(now!=src)
        {
            i=path[now];
            val[i]-=flow;
            val[i^1]+=flow;
            now=from[i];
        }
        return flow;
    }
     
    int isap()
    {
        int flow=0;
        if(!bfs()) return 0;
        memset(num,0,sizeof(num));
        for(int i=src;i<=decc;++i) num[lev[i]]++,cur[i]=front[i];
        int now=src,t;
        while(lev[src]<=decc)
        {
            if(now==decc)
            {
                flow+=augment();
                now=src;
            }
            bool advanced=false;
            for(int i=cur[now];i;i=nxt[i])
            {
                t=to[i];
                if(lev[t]==lev[now]-1 && val[i])
                {
                    advanced=true;
                    path[t]=i;
                    cur[now]=i;
                    now=t;
                    break;
                }
            }
            if(!advanced)
            {
                int mi=decc;
                for(int i=front[now];i;i=nxt[i])
                    if(val[i]) mi=min(mi,lev[to[i]]);
                if(!--num[lev[now]]) break;
                num[lev[now]=mi+1]++;
                cur[now]=front[now];
                if(now!=src) now=from[path[now]];
            }
        }
        return flow;
    }
     
    bool check(int mid)
    {
        rebuild(mid);
        return isap()==cnt;
    }
     
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;++i)
        {
            scanf("%s",s+1);
            for(int j=1;j<=m;++j)
            {
                if(s[j]=='.') mp[i][j]=1;
                else if(s[j]=='D') 
                {
                    sum_door++;
                    doorx[sum_door]=i;
                    doory[sum_door]=j;
                    mp[i][j]=2;
                    id_door[i][j]=sum_door;
                }
            }
        }
        for(int i=1;i<=n;++i)
            for(int j=1;j<=m;++j)
            {
                if(mp[i][j]!=1) continue;
                cnt++;
                memset(dis,0,sizeof(dis));
                prebfs(i,j);
                for(int i=1;i<=sum_door;++i)
                {
                    if(dis[doorx[i]][doory[i]]) 
                    {
                        tim[cnt][++sum[cnt]]=dis[doorx[i]][doory[i]];
                        wh[cnt][sum[cnt]]=id_door[doorx[i]][doory[i]];
                    }
                }
            }
        int l=0,r=450,mid,ans=-1;
        while(l<=r)
        {
            mid=l+r>>1;
            if(check(mid)) ans=mid,r=mid-1;
            else l=mid+1;
        }
        if(ans==-1) printf("impossible");
        else printf("%d",ans);
        return 0;
    } 
    

    1189: [HNOI2007]紧急疏散evacuate

    Time Limit: 10 Sec  Memory Limit: 128 MB
    Submit: 3362  Solved: 994
    [Submit][Status][Discuss]

    Description

    发生了火警,所有人员需要紧急疏散!假设每个房间是一个N M的矩形区域。每个格子如果是'.',那么表示这是一块空地;如果是'X',那么表示这是一面墙,如果是'D',那么表示这是一扇门,人们可以从这儿撤出房间。已知门一定在房间的边界上,并且边界上不会有空地。最初,每块空地上都有一个人,在疏散的时候,每一秒钟每个人都可以向上下左右四个方向移动一格,当然他也可以站着不动。疏散开始后,每块空地上就没有人数限制了(也就是说每块空地可以同时站无数个人)。但是,由于门很窄,每一秒钟只能有一个人移动到门的位置,一旦移动到门的位置,就表示他已经安全撤离了。现在的问题是:如果希望所有的人安全撤离,最短需要多少时间?或者告知根本不可能。

    Input

    输入文件第一行是由空格隔开的一对正整数N与M,3<=N <=20,3<=M<=20,以下N行M列描述一个N M的矩阵。其中的元素可为字符'.'、'X'和'D',且字符间无空格。

    Output

    只有一个整数K,表示让所有人安全撤离的最短时间,如果不可能撤离,那么输出'impossible'(不包括引号)。

    Sample Input

    5 5
    XXXXX
    X...D
    XX.XX
    X..XX
    XXDXX

    Sample Output

    3
  • 相关阅读:
    CCCC 2020 酱油记
    CCPC 2020 威海 滚粗记
    IEEExtreme 2020 酱油记
    CCSP 2020 酱油记
    ICPC 陕西省赛 2020 游记
    CCPC 网络赛 2020 自闭记
    CSP 第20次认证 酱油记
    CSP-S 2019 酱油记
    NOI2019 退役记
    树链剖分入门
  • 原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/8034566.html
Copyright © 2011-2022 走看看