【HNOI2007】bzoj1189 紧急疏散

234 篇文章 0 订阅
177 篇文章 0 订阅

Description

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

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

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

先预处理出每个空地到每个门的时间,然后把门按照时间拆点,二分答案,把同一时间只能有一个人到达门等价地看成每个时刻门只能出去一个人,剩下的人要在原地等待。从原点向空地连容量为1的边,从空地向到达时间的门连容量为1的边,满足时间要求的门向汇点连容量为1的边,每个时刻的门向下一时刻的自己连容量无穷大的边。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int s=50005,t=50006,oo=0x3f3f3f3f;
int fir[50010],ne[2000010],to[2000010],w[2000010],xx[]={1,-1,0,0},yy[]={0,0,1,-1},
dis[410][410],num[25][25],qx[410],qy[410],f[50010],que[50010],
n,m,tot,clo,fcnt,dcnt;
char map[25][25];
bool is(int x,int y)
{
    return x>=1&&x<=n&&y>=1&&y<=m&&map[x][y]=='.';
}
void init()
{
    int i,j,hd,tl,x,y,x1,y1,k;
    scanf("%d%d",&n,&m);
    for (i=1;i<=n;i++)
    {
        scanf("%s",map[i]+1);
        for (j=1;j<=m;j++)
        {
            if (map[i][j]=='.')
              num[i][j]=++fcnt;
            if (map[i][j]=='D')
              num[i][j]=++dcnt;
        }
    }
    for (i=1;i<=n;i++)
      for (j=1;j<=m;j++)
        if (map[i][j]=='D')
        {
            for (k=1;k<=fcnt;k++)
              dis[num[i][j]][k]=oo;
            hd=1;
            tl=0;
            for (k=0;k<4;k++)
              if (is(x1=i+xx[k],y1=j+yy[k]))
              {
                dis[num[i][j]][num[x1][y1]]=1;
                qx[++tl]=x1;
                qy[tl]=y1;
              }
            while (hd<=tl)
            {
                x=qx[hd];
                y=qy[hd++];
                for (k=0;k<4;k++)
                  if (is(x1=x+xx[k],y1=y+yy[k])&&dis[num[i][j]][num[x1][y1]]==oo)
                  {
                    dis[num[i][j]][num[x1][y1]]=dis[num[i][j]][num[x][y]]+1;
                    qx[++tl]=x1;
                    qy[tl]=y1;
                  }
            }
        }
}
bool judge()
{
    int i,j,flag;
    for (i=1;i<=fcnt;i++)
    {
        flag=0;
        for (j=1;j<=dcnt;j++)
          if (dis[j][i]<oo)
          {
            flag=1;
            break;
          }
        if (!flag) return 1;
    }
    return 0;
}
void add(int u,int v,int x)
{
    tot++;
    ne[tot*2]=fir[u];
    fir[u]=tot*2;
    to[tot*2]=v;
    w[tot*2]=x;
    ne[tot*2+1]=fir[v];
    fir[v]=tot*2+1;
    to[tot*2+1]=u;
    w[tot*2+1]=0;
}
bool bfs()
{
    int i,hd,tl,u,v;
    memset(f,0,sizeof(f));
    f[s]=1;
    hd=tl=1;
    que[1]=s;
    while (hd<=tl)
    {
        u=que[hd++];
        for (i=fir[u];i;i=ne[i])
          if (w[i]&&!f[v=to[i]])
          {
            f[v]=f[u]+1;
            que[++tl]=v;
          }
    }
    return f[t];
}
int dfs(int u,int lim)
{
    int ret=0,x,i,v;
    if (u==t) return lim;
    for (i=fir[u];i&&ret<lim;i=ne[i])
      if (w[i]&&f[v=to[i]]==f[u]+1)
      {
        x=dfs(v,min(lim-ret,w[i]));
        ret+=x;
        w[i]-=x;
        w[i^1]+=x;
      }
    if (!ret) f[u]=0;
    return ret;
}
bool ok(int x)
{
    int i,j;
    tot=0;
    memset(fir,0,sizeof(fir));
    for (i=1;i<=fcnt;i++)
      add(s,i,1);
    for (i=1;i<=fcnt;i++)
      for (j=1;j<=dcnt;j++)
        if (dis[j][i]<=x)
          add(i,dcnt*dis[j][i]+j+fcnt,1);
    for (i=1;i<=dcnt;i++)
      for (j=1;j<=x;j++)
      {
        add(dcnt*j+i+fcnt,t,1);
        if (j>1) add(dcnt*(j-1)+i+fcnt,dcnt*j+i+fcnt,oo);
      }/*
    if (x==3){
    for (i=1;i<=50006;i++)
      for (j=fir[i];j;j=ne[j])
        if (w[j]) printf("%d->%d:%d\n",i,to[j],w[j]);}*/
    while (bfs()) while (dfs(s,oo));
    for (i=fir[s];i;i=ne[i])
      if (w[i]) return 0;
    return 1;
}
int main()
{
    int l,r,mid;
    init();
    if (judge())
    {
        printf("impossible\n");
        return 0;
    }
    l=0;
    r=m*n;
    while (l<r)
    {
        mid=(l+r)/2;
        if (ok(mid)) r=mid;
        else l=mid+1;
    }
    printf("%d\n",l);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值