题目描述
发生了火警,所有人员需要紧急疏散!假设每个房间是一个N M的矩形区域。每个格子如果是'.',那么表示这是一块空地;如果是'X',那么表示这是一面墙,如果是'D',那么表示这是一扇门,人们可以从这儿撤出房间。已知门一定在房间的边界上,并且边界上不会有空地。最初,每块空地上都有一个人,在疏散的时候,每一秒钟每个人都可以向上下左右四个方向移动一格,当然他也可以站着不动。疏散开始后,每块空地上就没有人数限制了(也就是说每块空地可以同时站无数个人)。但是,由于门很窄,每一秒钟只能有一个人移动到门的位置,一旦移动到门的位置,就表示他已经安全撤离了。现在的问题是:如果希望所有的人安全撤离,最短需要多少时间?或者告知根本不可能。
输入输出格式
输入格式:输入文件第一行是由空格隔开的一对正整数N与M,3<=N <=20,3<=M<=20,以下N行M列描述一个N M的矩阵。其中的元素可为字符'.'、'X'和'D',且字符间无空格。
输出格式:只有一个整数K,表示让所有人安全撤离的最短时间,如果不可能撤离,那么输出'impossible'(不包括引号)。
一开始网络流时直接将门与汇点建一条mid的边,WA了之后发现有些空位无法到达部分门,果然我还是太年轻。
应该将门按照时间拆成mid个点然后dinic判断最大流是否为人数。
上代码:
#include<queue>
#include<cstdio>
#include<string>
#include<cstring>
using namespace std;
const int xx[4]={0,1,0,-1},yy[4]={1,0,-1,0};
const int maxn=4e4+5,maxm=2e6+5,INF=0x3f3f3f3f;
char mp[25][25];
int N,M,S,T,ans,tot,sum,st,cnt,hed,til,L,R;
int son[maxm],nxt[maxm],flw[maxm],lnk[maxn],G[25][25];
int dep[maxn],cur[maxn],que[maxn],ed[maxn],dis[maxn][25][25];
inline char read() {
char ch=getchar();
while (ch!='X'&&ch!='.'&&ch!='D') ch=getchar();
return ch;
}
inline bool bfs() {
for (int i=0; i<=T; i++) cur[i]=lnk[i],dep[i]=-1;
que[til=1]=S,hed=dep[S]=0;
while (hed<til)
for (int k=lnk[que[++hed]]; ~k; k=nxt[k]) if (dep[son[k]]<0&&flw[k]>0)
dep[son[k]]=dep[que[hed]]+1,que[++til]=son[k];
return dep[T]>0;
}
int dfs(int now,int flow) {
if (now==T) return flow;
for (int &k=cur[now]; ~k; k=nxt[k]) if (dep[son[k]]==dep[now]+1&&flw[k]) {
int d=dfs(son[k],min(flow,flw[k]));
if (d>0) {
flw[k]-=d,flw[k^1]+=d;
return d;
}
}
return 0;
}
inline void add_edge(int x,int y,int z) {
son[tot]=y,flw[tot]=z,nxt[tot]=lnk[x],lnk[x]=tot++;
son[tot]=x,flw[tot]=0,nxt[tot]=lnk[y],lnk[y]=tot++;
}
inline bool check(int x) {
S=0,T=maxn-1;
memset(lnk,-1,sizeof lnk),tot=0;
for (int i=1; i<=sum; i++) add_edge(S,i,1);
for (int k=1; k<=cnt; k++)
for (int i=0; i<N; i++)
for (int j=0; j<M; j++) if (mp[i][j]=='.'&&dis[k][i][j]<=x)
add_edge(G[i][j],(k-1)*x+sum+dis[k][i][j],1);
for (int i=1; i<=cnt; i++)
for (int j=1; j<=x; j++) {
int tmp=(i-1)*x+sum;
add_edge(tmp+j,T,1);
if (j<x) add_edge(tmp+j,tmp+j+1,INF);
}
int ret=0;
while (bfs()) while (int d=dfs(S,INF)) ret+=d;
return ret==sum;
}
inline void pre(int id) {
queue <int> Q;
while (!Q.empty()) Q.pop();
dis[id][ed[id]/M][ed[id]%M]=0;
Q.push(ed[id]);
while (!Q.empty()) {
int now=Q.front(),x=now/M,y=now%M;
Q.pop();
for (int f=0; f<4; f++) {
int l=x+xx[f],r=y+yy[f];
if (l<0||r<0||l>=N||r>=M||mp[l][r]!='.') continue;
if (dis[id][l][r]>dis[id][x][y]+1)
dis[id][l][r]=dis[id][x][y]+1,Q.push(l*M+r);
}
}
}
int main() {
memset(dis,63,sizeof dis);
scanf("%d%d",&N,&M);
for (int i=0; i<N; i++)
for (int j=0; j<M; j++) {
mp[i][j]=read();
if (mp[i][j]=='D') ed[++cnt]=i*M+j;
if (mp[i][j]=='.') G[i][j]=++sum;
}
for (int i=1; i<=cnt; i++) pre(i);
L=1,R=N*M;
if (!check(R)) {puts("impossible");return 0;}
while (L<=R) {
int mid=L+(R-L>>1);
if (check(mid)) ans=mid,R=mid-1;
else L=mid+1;
}
printf("%d\n",ans);
return 0;
}
这是我造数据的代码,思路是造门的位置,然后bfs造空位:
#include<windows.h>
#include<bits/stdc++.h>
using namespace std;
const int xx[4]={0,1,0,-1},yy[4]={1,0,-1,0};
int N,M,mp[950][950],X[1000],Y[1000],tot,sum,hed,til;
struct point{int x,y;};
inline int random(int x) {
return ((rand()<<16)+(rand()<<1)+(rand()&1))%x+1;
}
inline int bfs(int id,int sum) {
queue <point> Q;
while (!Q.empty()) Q.pop();
point st;
st.x=X[id],st.y=Y[id];
Q.push(st);
while (!Q.empty()) {
if (sum<=0) break;
point now=Q.front(),nxt;
Q.pop();
for (int f=0; f<4; f++) if (random(15)^1) {
int l=now.x+xx[f],r=now.y+yy[f];
if (l<2||r<2||l>=N||r>=M||mp[l][r]!=3) continue;
sum--,mp[l][r]=1;
nxt.x=l,nxt.y=r;
Q.push(nxt);
}
}
}
int main() {
freopen("OJ.in","w",stdout);
srand(GetTickCount());
N=20,M=20;
tot=rand()%10+2;
printf("%d %d\n",N,M);
for (int i=1; i<=N; i++)
for (int j=1; j<=M; j++) mp[i][j]=3;
for (int i=1; i<=tot; i++) {
int x=rand()%N+1,y=rand()%M+1;
if (mp[x][y]==3) mp[x][y]=2,X[++sum]=x,Y[sum]=y;
}
for (int i=1; i<=tot; i++) bfs(i,rand()%70);
for (int i=1; i<=N; i++,putchar('\n'))
for (int j=1; j<=M; j++)
switch (mp[i][j]) {
case 1: putchar('.');break;
case 2: putchar('D');break;
case 3: putchar('X');break;
}
return 0;
}