BZOJ 1189([HNOI2007]紧急疏散evacuate-网络流二分+拆点)

发生了火警,所有人员需要紧急疏散!假设每个房间是一个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计算出每个人离每个出口的距离,网络流,二分+拆点,注意一个人不能路过一个出口到达另一个出口(因为出口格子有人数限制)d。

#include<cstdio>
#include<queue>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<functional>
#include<iostream>
#include<cmath>
#include<cctype>
#include<ctime>
using namespace std;
#define For(i,n) for(int i=1;i<=n;i++)
#define Fork(i,k,n) for(int i=k;i<=n;i++)
#define Rep(i,n) for(int i=0;i<n;i++)
#define ForD(i,n) for(int i=n;i;i--)
#define RepD(i,n) for(int i=n;i>=0;i--)
#define Forp(x) for(int p=Pre[x];p;p=Next[p])
#define Forpiter(x) for(int &p=iter[x];p;p=Next[p])
#define pi pair<int,int>
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define Lson (x<<1)
#define Rson ((x<<1)+1)
#define MEM(a) memset(a,0,sizeof(a));
#define MEMI(a) memset(a,127,sizeof(a));
#define MEMi(a) memset(a,128,sizeof(a));
#define INF (2139062143)
#define F (100000007)
#define MAXN (400+400*800+100)
#define MAXM (6000000+100)
#define PRi2D(a,n,m) For(i,n) { \
                        For(j,m-1) cout<<a[i][j]<<' ';\
                        cout<<a[i][m]<<endl; \
                        } 
long long mul(long long a,long long b){return (a*b)%F;}
long long add(long long a,long long b){return (a+b)%F;}
long long sub(long long a,long long b){return (a-b+(a-b)/F*F+F)%F;}
typedef long long ll;
int read()
{
    int x=0,f=1; char ch=getchar();
    while(!isdigit(ch)) {if (ch=='-') f=-1; ch=getchar();}
    while(isdigit(ch)) { x=x*10+ch-'0'; ch=getchar();}
    return x*f;
} 
class Max_flow  //dinic+µ±Ç°»¡ÓÅ»¯   
{    
public:    
    int n,t;    
    int q[MAXN];    
    int edge[MAXM],Next[MAXM],Pre[MAXN],weight[MAXM],size;    
    void addedge(int u,int v,int w)      
    {      
        edge[++size]=v;      
        weight[size]=w;      
        Next[size]=Pre[u];      
        Pre[u]=size;      
    }      
    void addedge2(int u,int v,int w){addedge(u,v,w),addedge(v,u,0);}     
    bool b[MAXN];    
    int d[MAXN];    
    bool SPFA(int s,int t)      
    {      
        For(i,n) d[i]=INF;    
        MEM(b)    
        d[q[1]=s]=0;b[s]=1;      
        int head=1,tail=1;      
        while (head<=tail)      
        {      
            int now=q[head++];      
            Forp(now)      
            {      
                int &v=edge[p];      
                if (weight[p]&&!b[v])      
                {      
                    d[v]=d[now]+1;      
                    b[v]=1,q[++tail]=v;      
                }      
            }          
        }      
        return b[t];      
    }     
    int iter[MAXN];  
    int dfs(int x,int f)  
    {  
        if (x==t) return f;  
        Forpiter(x)  
        {  
            int v=edge[p];  
            if (weight[p]&&d[x]<d[v])  
            {  
                  int nowflow=dfs(v,min(weight[p],f));  
                  if (nowflow)  
                  {  
                    weight[p]-=nowflow;  
                    weight[p^1]+=nowflow;  
                    return nowflow;  
                  }  
            }  
        }  
        return 0;  
    }  
    int max_flow(int s,int t)  
    {  
        (*this).t=t;
        int flow=0;  
        while(SPFA(s,t))  
        {  
            For(i,n) iter[i]=Pre[i];  
            int f;  
            while (f=dfs(s,INF))  
                flow+=f;   
        }  
        return flow;  
    }   
    void mem(int n)    
    {    
        (*this).n=n;  
        size=1;    
        MEM(Pre)   
    }    
}S;    

#undef MAXN
#define MAXN (30)

char s[MAXN][MAXN];
int h[MAXN][MAXN]={0};

const int dir[4][2]={{-1,0},{0,-1},{1,0},{0,1}};
int dis[450][450];
int n,m;
bool inside(int i,int j) {return 1<=i&&i<=n&&1<=j&&j<=m&&s[i][j]!='X'; }
int id(int i,int j){return i*m-m+j;}
void bfs(int i,int j) {
    int st=id(i,j);
    dis[st][st]=0;
    queue < pi > q;
    q.push(mp(i,j));
    while(!q.empty()) {
        pi now=q.front(); q.pop();
        int x=now.fi,y=now.se;
        Rep(di,4) {
            int nx=x+dir[di][0],ny=y+dir[di][1];
            if (inside(nx,ny)&&dis[st][id(nx,ny)]==INF) {
                dis[st][id(nx,ny)]=dis[st][id(x,y)]+1;
                if (s[nx][ny]!='D') q.push(mp(nx,ny));
            }
        }
    }    
} 
#define vi vector<int>
#define SI(x) ((x).size())
vi v1,v2;
int sz1,sz2;
void prework() {
    For(i,n) For(j,m)
        if (s[i][j]=='.') v1.pb(id(i,j));
        else if (s[i][j]=='D') v2.pb(id(i,j));
    sz1=SI(v1);sz2=SI(v2);
}
int check(int t) {
    S.mem(sz1+sz2*t+2);
    int tot2=sz1+sz2*t,st=tot2+1,ed=st+1;

    For(i,sz1) S.addedge2(st,i,1);
    Fork(i,sz1+1,tot2) S.addedge2(i,ed,1);

    For(i,sz1) For(j,sz2) if (dis[v1[i-1]][v2[j-1]]<=t) {
        S.addedge2(i,sz1+t*(j-1)+dis[v1[i-1]][v2[j-1]],1);
    }
    For(i,sz2) {
        For(d,t-1) {
            S.addedge2(sz1+t*(i-1)+d,sz1+t*(i-1)+d+1,sz1);
        }       
    }
    int ans=S.max_flow(st,ed);
    return ans;
}  
int main()
{
//  freopen("bzoj1189.in","r",stdin);
//  freopen(".out","w",stdout);
    cin>>n>>m;
    For(i,n) scanf("%s",s[i]+1);
    int p=0;
    For(i,n) For(j,m) if (s[i][j]=='D') {
        h[i][j]=++p;
    }
    int cnt=0;
    MEMI(dis)
    For(i,n) For(j,m) if (s[i][j]=='.') {
        ++cnt,bfs(i,j);
    }
    int l=0,r=2*n*m,ans=INF;
    prework();
    while(l<=r) {
        int m=l+r>>1;
        if (check(m)==cnt) ans=m,r=m-1;else l=m+1;
    }

    if (ans==INF) puts("impossible"); else printf("%d\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值