题意:(starfall512.com/?p=320)教室里有很多人,有若干个出口,问所有人最少需要多少时间全部走到教室外面。
难度:4
题解:如果人在出口的位置,就算出去;每个位置每次只能站一个人;先BFS判断是否全部人都能走出去(排除无解的情况)。分层建图,每层代表一个时间点。比如第n层图,首先每个位置的点拆点,两点之间容量为1(为了满足每个点最多一个人)对于每一个位置(x,y),向四个方向(xx,yy),如果不越界并且不是‘#’(wall),就从n-1层的x,y位置建边到n层的xx,yy点,n-1层的(x,y)也要连向n层的(x,y)点,每层建图后计算最大流,最大流就是那个时间点上能够出教室的人数。出教室的总人数需要累加,用来判断跳出循环。可以二分最大层数,也可以一层层累加上去。
难度:4
题解:如果人在出口的位置,就算出去;每个位置每次只能站一个人;先BFS判断是否全部人都能走出去(排除无解的情况)。分层建图,每层代表一个时间点。比如第n层图,首先每个位置的点拆点,两点之间容量为1(为了满足每个点最多一个人)对于每一个位置(x,y),向四个方向(xx,yy),如果不越界并且不是‘#’(wall),就从n-1层的x,y位置建边到n层的xx,yy点,n-1层的(x,y)也要连向n层的(x,y)点,每层建图后计算最大流,最大流就是那个时间点上能够出教室的人数。出教室的总人数需要累加,用来判断跳出循环。可以二分最大层数,也可以一层层累加上去。
#include <cstdio>
using namespace std;
const int mm = 11111111;
const int mn = 111111;
const int oo = 1000000000;
const int dx[4] = {0,1,0,-1};
const int dy[4] = {1,0,-1,0};
int node,src,dest,edge,ret;
int reach[mm],flow[mm],next[mm];
int head[mn],work[mn],dis[mn],q[mn];
char map[22][22];
int mark[22][22],d[22][22],qx[mn],qy[mn];
int i,j,k,n,m,x,y,t,ans,num;
inline int min(int a,int b) {
return a<b?a:b;
}
inline void addedge(int u,int v,int c) {
reach[edge]=v;flow[edge]=c;next[edge]=head[u];head[u]=edge++;
reach[edge]=u;flow[edge]=0;next[edge]=head[v];head[v]=edge++;
}
inline void makegraph(int c,int dd) { //分层图,每次加进去一层图,从小到大枚举
int i,j,k,w=node-2;
while(dd--) head[node++]=-1;
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
if(mark[i][j]) {
addedge(w+mark[i][j],w+mark[i][j]+t,1);
if(!c && map[i][j]=='X') addedge(src,w+mark[i][j],1);
if(c) {
addedge(w-t+mark[i][j],w+mark[i][j],1);
if(map[i][j]=='@') addedge(w+mark[i][j]+t,dest,1);
for(k=0;k<4;k++) {
x=i+dx[k];
y=j+dy[k];
if(x>0&&x<=n&&y>0&&y<=m&&mark[x][y])
addedge(w+mark[x][y]-t,w+mark[i][j],1);
}
}
}
}
bool Dinic_bfs() {
int i,u,v,l,r=0;
for(i=0;i<node;i++) dis[i]=-1;
dis[q[r++]=src] = 0;
for(l=0;l<r;l++)
for(i=head[u=q[l]];i>=0;i=next[i])
if(flow[i] && dis[v=reach[i]]<0) {
dis[q[r++]=v] = dis[u] + 1;
if(v==dest) return 1;
}
return 0;
}
int Dinic_dfs(int u,int exp) {
if(u==dest) return exp;
for(int &i=work[u],v,tmp;i>=0;i=next[i])
if(flow[i]&&dis[v=reach[i]]==dis[u]+1&&(tmp=Dinic_dfs(v,min(exp,flow[i]))>0)) {
flow[i]-=tmp;
flow[i^1]+=tmp;
return tmp;
}
return 0;
}
int Dinic_flow() {
int i,delta;
while(Dinic_bfs()) {
for(i=0;i<node;i++) work[i]=head[i];
while(delta=Dinic_dfs(src,oo)) ret += delta;
}
return ret;
}
void bfs(int r) {
int i,l;
for(l=0;l<r;l++)
for(i=0;i<4;i++) {
x=qx[l]+dx[i];
y=qy[l]+dy[i];
if(x>0&&x<=n&&y>0&&y<=m&&map[x][y]!='#'&&d[x][y]<0)
qx[r]=x,qy[r]=y,d[x][y]=d[qx[l]][qy[l]]+1,++r;
}
}
bool ok() {
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(map[i][j]=='X' && d[i][j]<0) return 0;
return 1;
}
int main() {
while(~scanf("%d%d",&n,&m)) {
for(t=0,i=1;i<=n;i++)
for(getchar(),j=1;j<=m;j++) {
map[i][j] = getchar();
mark[i][j] = 0;
if(map[i][j]!='#') mark[i][j]=(++t) + 1; //t指不是障碍物的个数,mark[i][j]指不是障碍物的编号从2到t+1
}
for(k=0,i=1;i<=n;i++)
for(j=1;j<=m;j++)
if(map[i][j]=='@') qx[k]=i,qy[k]=j,d[i][j]=0,++k;//把每个出口的坐标储存在qx[],qy[]中,k表示有几个出口
else d[i][j]=-1;//不是出口的点的d[i][j]全都设为-1
bfs(k);
if(!ok()) {
printf("-1\n");
continue;
}
for(num=0,i=1;i<=n;i++)
for(j=1;j<=m;j++)
if(map[i][j]=='X') ++num;
ans=ret=0;
head[0]=head[1]=-1;
node=2,src=0,dest=1;
makegraph(0,t*2);
while(Dinic_flow()<num) makegraph(1,t*2),++ans;
printf("%d\n",ans);
}
return 0;
}