二分答案最大流[HNOI2007]紧急疏散EVACUATE
Posted wxjor
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了二分答案最大流[HNOI2007]紧急疏散EVACUATE相关的知识,希望对你有一定的参考价值。
[HNOI2007]紧急疏散EVACUATE
题目描述
发生了火警,所有人员需要紧急疏散!假设每个房间是一个N M的矩形区域。每个格子如果是‘.‘,那么表示这是一块空地;如果是‘X‘,那么表示这是一面墙,如果是‘D‘,那么表示这是一扇门,人们可以从这儿撤出房间。已知门一定在房间的边界上,并且边界上不会有空地。最初,每块空地上都有一个人,在疏散的时候,每一秒钟每个人都可以向上下左右四个方向移动一格,当然他也可以站着不动。疏散开始后,每块空地上就没有人数限制了(也就是说每块空地可以同时站无数个人)。但是,由于门很窄,每一秒钟只能有一个人移动到门的位置,一旦移动到门的位置,就表示他已经安全撤离了。现在的问题是:如果希望所有的人安全撤离,最短需要多少时间?或者告知根本不可能。
输入输出格式
输入格式:
输入文件第一行是由空格隔开的一对正整数N与M,3<=N <=20,3<=M<=20,以下N行M列描述一个N M的矩阵。其中的元素可为字符‘.‘、‘X‘和‘D‘,且字符间无空格。
输出格式:
只有一个整数K,表示让所有人安全撤离的最短时间,如果不可能撤离,那么输出‘impossible‘(不包括引号)。
输入输出样例
5 5 XXXXX X...D XX.XX X..XX XXDXX
3
试题分析:创建超级源S,超级汇T
方便起见,先对于每一块空地BFS求出它到每个能到达的门的距离
然后二分答案,建图。
S连到每一个节点,容量为1的边。代表每个位置上有一个人。
简单粗暴的建图方法就是直接对于每个点将其每个时刻的点都建出来,会T一个点。
考虑优化,发现我们不用管每个时刻这个门的状态是什么,而是只用关心最后从这个门出去多少个。
所以我们的建图方法如下:
①S连接到每一块空地容量为1
②每一块空地连接到其在二分答案时间内可以到的门,容量为1
③将每一个门连向T,容量为X(二分的时间)
代码:
#include<iostream> #include<cstring> #include<cstdio> #include<vector> #include<queue> #include<algorithm> using namespace std; inline int read(){ int x=0,f=1;char c=getchar(); for(;!isdigit(c);c=getchar()) if(c==‘-‘) f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-‘0‘; return x*f; } #define LL long long const int MAXN = 500001; const int MAXN2 = 1000001; const int INF = 9999999; int N,M; int Root[MAXN2+1],Next[MAXN2+1],Node[MAXN+1],C[MAXN2+1]; int cnt; char Map[21][21]; void insert(int u,int v,int w){ C[cnt]=w; Node[cnt]=v; Next[cnt]=Root[u]; Root[u]=cnt++; return ; } struct data{ int x,y,tm; }; bool vis[21][21]; int dis[21][21][21][21]; int dist[5][2]={{0,-1},{0,1},{1,0},{-1,0}}; int ans; int S,T,KT; void BFST(){ data t; for(int i=1;i<=N;i++){ for(int j=1;j<=M;j++){ if(Map[i][j]==‘.‘){ ++KT; queue<data> Que; t.x=i; t.y=j; t.tm=0; Que.push(t); memset(vis,0,sizeof(vis)); vis[i][j]=true; while(!Que.empty()){ data k=Que.front(); Que.pop(); if(Map[k.x][k.y]==‘D‘){ dis[i][j][k.x][k.y]=k.tm; continue; } for(int l=0;l<4;l++){ int xx=k.x+dist[l][0]; int yy=k.y+dist[l][1]; if(xx<1||yy<1||xx>N||yy>M||Map[xx][yy]==‘X‘||vis[xx][yy]) continue; t.x=xx; t.y=yy; t.tm=k.tm+1; vis[xx][yy]=true; Que.push(t); } } } } } return ; } int D[MAXN+1]; bool BFS(){ queue<int> Que; Que.push(S); memset(D,0,sizeof(D)); D[S]=1; while(!Que.empty()){ int k=Que.front(); Que.pop(); for(int x=Root[k];x>-1;x=Next[x]){ int v=Node[x]; if(C[x]>0&&!D[v]){ D[v]=D[k]+1; if(v==T) return true; Que.push(v); } } } return false; } int cur[MAXN+1]; int DFS(int k,int t){ if(k==T) return t; int res=0; for(int x=cur[k];x>-1;x=Next[x]){ int v=Node[x]; if(C[x]>0&&D[v]==D[k]+1){ int tmp=DFS(v,min(C[x],t)); if(!tmp) continue; C[x]-=tmp; t-=tmp; C[x^1]+=tmp; res+=tmp; if(!t) return res; } cur[k]=x; } if(!res) D[k]=-1; return res; } bool check(int K){ cnt=0; for(int i=0;i<=MAXN;i++) Root[i]=-1; memset(Next,0,sizeof(Next)); S=0,T=500000; memset(C,0,sizeof(C)); memset(Node,0,sizeof(Node)); for(int i=1;i<=N;i++){ for(int j=1;j<=M;j++){ if(Map[i][j]==‘.‘){ insert(S,(i-1)*M+j,1); insert((i-1)*M+j,S,0); for(int l1=1;l1<=N;l1++){ for(int l2=1;l2<=M;l2++){ if(Map[l1][l2]==‘D‘&&dis[i][j][l1][l2]&&dis[i][j][l1][l2]<=K){ insert((i-1)*M+j,(l1-1)*M+l2,1); insert((l1-1)*M+l2,(i-1)*M+j,0); } } } } else if(Map[i][j]==‘D‘){ insert((i-1)*M+j,T,K); insert(T,(i-1)*M+j,0); } } } int ans=0; --cnt; while(BFS()){ for(int i=0;i<=N*M;i++) cur[i]=Root[i]; ans+=DFS(S,INF); } return ans>=KT; } int main(){ N=read(),M=read(); for(int i=1;i<=N;i++){ for(int j=1;j<=M;j++) cin>>Map[i][j]; } BFST(); int l=0,r=400; while(l<=r){ int mid=(l+r)>>1; if(check(mid)) ans=mid,r=mid-1; else l=mid+1; } if(ans) printf("%d\n",ans); else puts("impossible"); }
以上是关于二分答案最大流[HNOI2007]紧急疏散EVACUATE的主要内容,如果未能解决你的问题,请参考以下文章
BZOJ 1189 HNOI2007 紧急疏散evacuate
BZOJ 1189: [HNOI2007]紧急疏散evacuate|网络流|二分答案
BZOJ1189 HNOI2007 紧急疏散evacuate 网络流+BFS+二分法
AC日记——[HNOI2007]紧急疏散evacuate bzoj 1189