[bzoj1189] [HNOI2007]紧急疏散evacuate

来自FallDream的博客,未经允许,请勿转载,谢谢。

-----------------------------------------------------------------------------

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

n,m<=20

看到题目很容易想到二分答案,然后分层图网络流,每一个点都拆成很多个点,每个点向下个时间点能到的点连边,然后check一下是否能全部撤离。想了想没问题,写一发T了。

但是仔细想想,并不需要那么复杂。因为这道题每个人到每个出口的路径长度确定,所以只需要把每个出口拆点就行了,每个人向每个能到的出口,第(它能到的时间)个点连边,每个出口拆成的点向下一个连边,就行啦。

代码巨丑...

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#define S 0
#define T 30000
#define INF 2000000000
using namespace std;
inline int read()
{
    int x = 0 , f = 1; char ch = getchar();
    while(ch < '0' || ch > '9'){ if(ch == '-') f = -1;  ch = getchar();}
    while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
    return x * f;
}
int id[22][22],s[405][405];
int d[T+5],q[T+5],n,m,head[T+5],c[T+5],cnt=1,tot=0,top,num=0;
struct edge{int to,next,w;}e[T*200];
char st[22][22];
struct pos{int x,y;};queue<pos> qu;
const int dis[4][2]={{1,0},{-1,0},{0,1},{0,-1}};

inline void ins(int f,int t,int w){
    e[++cnt]=(edge){t,head[f],w};head[f]=cnt;
    e[++cnt]=(edge){f,head[t],0};head[t]=cnt;
}

int dfs(int x,int f)
{
    if(x==T)return f;
    int used=0;
    for(int&i=c[x];i;i=e[i].next)
        if(e[i].w&&d[e[i].to]==d[x]+1)
        {
            int w=dfs(e[i].to,min(f-used,e[i].w));
            used+=w;e[i].w-=w;e[i^1].w+=w;
            if(used==f)return f;
        }
    return d[x]=-1,used;
}

bool bfs()
{
    int i,j;memset(d,0,sizeof(d));
    for(d[q[top=i=1]=S]=1;i<=top;i++)
        for(int j=c[q[i]]=head[q[i]];j;j=e[j].next)
            if(e[j].w&&!d[e[j].to])
                d[q[++top]=e[j].to]=d[q[i]]+1;
    return d[T];
}

void build(int x)
{
    cnt=1;memset(head,0,sizeof(head));
    for(int i=1;i<=num;i++)
    {
        for(int k=1;k<=x;k++)
            ins((k-1)*num+i,T,1);
        for(int k=1;k<x;k++)
            ins((k-1)*num+i,k*num+i,INF);
    }
    for(int i=num+1;i<=tot;i++)
    {
        ins(S,num*x+i,1);
        for(int j=1;j<=num;j++)
            if(s[i][j]<=x)
                ins(num*x+i,(s[i][j]-1)*num+j,INF);
    }
}

void getdis(int x,int y)
{
    int now=id[x][y];qu.push((pos){x,y});
    s[now][now]=0;
    while(!qu.empty())
    {
        pos th=qu.front();qu.pop();
        for(int i=0;i<4;i++)
        {
            int xx=th.x+dis[i][0],yy=th.y+dis[i][1];
            if(xx<1||yy<1||xx>n||yy>m||s[xx][yy]=='X') continue;
            if(s[now][id[th.x][th.y]]+1<s[now][id[xx][yy]])
            {
                s[now][id[xx][yy]]=s[now][id[th.x][th.y]]+1;
                if(st[xx][yy]=='.')qu.push((pos){xx,yy});
            }
        }
    }
}

int main()
{
    n=read();m=read();
    for(int i=1;i<=n;i++)
        scanf("%s",st[i]+1);
    memset(s,127,sizeof(s));
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            if(st[i][j]=='D')id[i][j]=++num;
    tot=num;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            if(st[i][j]=='.')
                id[i][j]=++tot;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            if(st[i][j]=='.')
                getdis(i,j);
    int l=1,r=324,mid,ans=-1;
    while(l<=r)
    {
        mid=l+r>>1;build(mid);
        int sum=0;
        while(bfs())sum+=dfs(S,INF);
        if(sum==tot-num)ans=mid,r=mid-1;
        else l=mid+1;
    }
    if(ans==-1)return 0*puts("impossible");
    printf("%d\n",ans);
    return 0;
}

 

posted @ 2017-04-03 19:26  FallDream  阅读(317)  评论(0编辑  收藏  举报