题意:一个N M的矩形区域。格子如果是'.',那么表示这是一块空地;如果是'X',那么表示这是一面墙,如果是'D',那么表示这是一扇门,人们可以从这儿撤出房间。已知门一定在房间的边界上,并且边界上不会有空地。每块空地上都有一个人,在疏散的时候,每一秒钟每个人都可以向上下左右四个方向移动一格,当然他也可以站着不动。疏散开始后,每块空地上就没有人数限制。每一秒钟只能有一个人移动到门的位置,一旦移动到门的位置,就表示他已经安全撤离了。求所有的人安全撤离,最短需要多少时间
题解:
好神啊这题……
首先我们用BFS跑出每个门到每个以可以到达的点的最小时间,然后二分答案x,从每个门向汇点连一条容量x的边,从源点向每个空地连边,空地向能在x时间内能到达的每一个门连边,跑最大流检验即可。
(实际上这个算法是错误的,因为无法限制每一秒之能有一个人通过,因而需要把每个门都拆成x个门,x-1向x连边,每个拆出来的点向汇点连一条容量为一的边。啊好麻烦懒得写了,谁叫数据水呢QAQ)
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include <queue> #include <vector> #include <cstdio> #include <climits> #include <cstring> #include <cstdlib> #include <iostream> #include <algorithm> using namespace std; #define NODE pair<int,int> const int T=1000+2; const int U=400+2; const int MAXN=30+2; const int MAXK=60000+2; const int MAXM=3000000+2; const int X[]={-1,0,1,0}; const int Y[]={0,1,0,-1}; struct EDGE{ int u,c; EDGE(){} EDGE(int _u,int _c):u(_u),c(_c){} }e[MAXM]; int N,M,D=1,ans=-1,sum,dist[MAXN][MAXN][MAXN],g[MAXN][MAXN],d[MAXK],cur[MAXK],cnt; char S[MAXN][MAXM]; queue<int> q2; queue<NODE> q1; vector<int> tab[MAXK]; void Insert(int u,int v,int c){ tab[u].push_back(cnt),e[cnt++]=EDGE(v,c); tab[v].push_back(cnt),e[cnt++]=EDGE(u,0); } bool Check(int x,int y){ if(!x || !y) return 0; if(x>N || y>M) return 0; return g[x][y]==1; } void Find(int k,int x,int y){ q1.push(make_pair(x,y)),dist[k][x][y]=0; NODE t; while(!q1.empty()){ t=q1.front(),q1.pop(); for(int i=0;i<4;i++) if(Check(t.first+X[i],t.second+Y[i]) && dist[k][t.first+X[i]][t.second+Y[i]]>dist[k][t.first][t.second]+1){ dist[k][t.first+X[i]][t.second+Y[i]]=dist[k][t.first][t.second]+1; q1.push(make_pair(t.first+X[i],t.second+Y[i])); } } } void Build(int x){ cnt=0; for(int i=1;i<=T;i++) tab[i].clear(); for(int i=1;i<=N;i++) for(int j=1;j<=M;j++) if(g[i][j]==1) Insert(0,(i-1)*M+j,1); for(int i=2;i<=D;i++) Insert(N*M+i,T,x); for(int i=2;i<=D;i++) for(int j=1;j<=N;j++) for(int k=1;k<=M;k++) if(dist[i][j][k]<=x) Insert((j-1)*M+k,N*M+i,x); } bool BFS(int s,int t){ memset(d,-1,sizeof(d)); d[s]=0,q2.push(s); int x; while(!q2.empty()){ x=q2.front(),q2.pop(); for(int i=0;i<tab[x].size();i++) if(e[tab[x][i]].c && d[e[tab[x][i]].u]==-1) d[e[tab[x][i]].u]=d[x]+1,q2.push(e[tab[x][i]].u); } return d[t]>0; } int DFS(int x,int f,int t){ if(x==t) return f; int flow,used=0; for(int i=cur[x];i<tab[x].size();i++) if(e[tab[x][i]].c && d[e[tab[x][i]].u]==d[x]+1){ flow=DFS(e[tab[x][i]].u,min(f-used,e[tab[x][i]].c),t); e[tab[x][i]].c-=flow,e[tab[x][i]^1].c+=flow,used+=flow; if(e[tab[x][i]].c) cur[x]=i; if(used==f) return f; } if(!used) d[x]=-1; return used; } int Dinic(int s,int t){ int ret=0; while(BFS(s,t)){ memset(cur,0,sizeof(cur)); ret+=DFS(s,INT_MAX,t); } return ret; } bool Judge(int x){ Build(x); return Dinic(0,T)==sum; } int main(){ scanf("%d %d",&N,&M); for(int i=1;i<=N;i++) scanf("%s",S[i]+1); for(int i=1;i<=N;i++) for(int j=1;j<=M;j++) if(S[i][j]=='.') g[i][j]=1,sum++; else if(S[i][j]=='D') g[i][j]=++D; memset(dist,0X7F,sizeof(dist)); for(int i=1;i<=N;i++) for(int j=1;j<=M;j++) if(g[i][j]>1) Find(g[i][j],i,j); int l=1,r=U,m; while(l<=r){ m=(l+r)>>1; if(Judge(m)) ans=m,r=m-1; else l=m+1; } if(ans==-1) printf("impossible\n"); else printf("%d\n",ans); return 0; }