BZOJ1189 [HNOI2007]紧急疏散evacuate

187 篇文章 0 订阅
58 篇文章 0 订阅

标签:网络流,dinic,二分,最大流

题目

题目传送门

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
HINT

2015.1.12新加数据一组,鸣谢1756500824

C++语言请用scanf(“%s”,s)读入!

分析

首先每个门做一次bfs,求这扇门到每一个人的时间,然后二分时间T,跑最大流dinic算法,如果流量=人数那么T就是一个可行的答案

具体的建图操作如下:

  • 从源点向每个空地连一条流量为1的边
  • 每个门向汇点连一条流量为T的边
  • 每个空地向可以到达的门连一条流量为T的边

code

本代码目前无法在BZOJ上AC,wxh大爷太毒瘤了,加强的一组数据强悍

4 5
XXDXX
XX.XX
X…X
XXDXX

ans=3

luogu数据较弱,可以AC,话说我要不要去加强下luogu的数据啊,逃)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define dep(i,a,b) for(int i=a;i>=b;i--)
#define ll long long
#define mem(x,num) memset(x,num,sizeof x)
#define reg(x) for(int i=last[x];i;i=e[i].next)
#define inf 0x7fffffff
using namespace std;
inline ll read()
{
    ll f=1,x=0;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;
}
const int dx[4]={0,1,0,-1},dy[4]={1,0,-1,0},maxn=1e3+6,S=0,T=1001;
int n,m,door=1,cnt,ans,tot,na=-1;
int Map[26][26],last[maxn],h[maxn],que[maxn];
int dis[406][26][26];
char ch[26];
struct node{int x,y,s;}d[maxn];
struct edge{int to,next,v;}e[maxn*maxn]; 
void insert(int u,int v,int w){
    e[++cnt]=(edge){v,last[u],w};last[u]=cnt;
    e[++cnt]=(edge){u,last[v],0};last[v]=cnt;
}

bool bfs()
{
    int head=0,tail=1,now;
    mem(h,-1);h[S]=0;que[0]=S;
    while(head<tail){
        now=que[head++];
        reg(now)
            if(h[e[i].to]==-1&&e[i].v){
                h[e[i].to]=h[now]+1;
                que[tail++]=e[i].to;
            }
    }
    return h[T]!=-1;
}

int dfs(int x,int f){
    if(x==T)return f;
    int w,used=0;
    reg(x)
        if(h[e[i].to]==h[x]+1){
            w=dfs(e[i].to,min(e[i].v,f-used));
            e[i].v-=w,e[i^1].v+=w,used+=w;
            if(used==f)return f;
        }
    if(!used)h[x]=-1;
    return used;
}
void dinic(){while(bfs())ans+=dfs(0,inf);}
void build(int x){
    mem(last,0);cnt=1;
    rep(i,1,n)
        rep(j,1,m)if(Map[i][j]==1)insert(S,(i-1)*m+j,1);
    rep(i,2,door)insert(n*m+i,T,x);
    rep(i,2,door)
        rep(j,1,n)
            rep(k,1,m)
                if(dis[i][j][k]<=x)insert((j-1)*m+k,n*m+i,x);
}
bool judge(int x){
    build(x);ans=0;dinic();if(ans==tot)return 1;return 0;
}
void search(int k,int x,int y){
    int head=0,tail=1,nx,ny;
    d[0].x=x,d[0].y=y;
    while(head<tail){
        rep(i,0,3){
            nx=d[head].x+dx[i],ny=d[head].y+dy[i];
            if(nx<1||ny<1||nx>n||ny>m||Map[nx][ny]!=1)continue;
            if(dis[k][nx][ny]==inf){
                dis[k][nx][ny]=d[tail].s=d[head].s+1;
                d[tail].x=nx,d[tail++].y=ny;
            }
        }
        head++;
    }
}
int main()
{
    n=read(),m=read();
    rep(i,1,n){
        scanf("%s",ch);
        rep(j,1,m){
            if(ch[j-1]=='.')Map[i][j]=1,tot++;
            else if(ch[j-1]=='D')Map[i][j]=++door;
        }
    }
    rep(i,2,door)
        rep(j,1,n)
            rep(k,1,m)dis[i][j][k]=inf;
    rep(i,1,n)
        rep(j,1,m)
            if(Map[i][j]>1)search(Map[i][j],i,j);
    int l=0,r=406;
    while(l<=r){
        int mid=(l+r)>>1;
        if(judge(mid))na=mid,r=mid-1;
        else l=mid+1;
    }
    //cout<<l<<' '<<r<<endl;
    if(na==-1)printf("impossible");
    else printf("%d\n",na);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值