http://www.lydsy.com/JudgeOnline/problem.php?id=1189
二分答案
源点向人连边,流量为1
门拆为mid个点,同一个门的第j个点向第j+1个点连边,流量为inf
若第i个人第k秒到达第j个门,第i个人向第j个门拆出的第k个点连边,流量为1
所有门向汇点连边,流量为1
用ISAP写的,真快,也真TM长
#include<queue> #include<cstdio> #include<algorithm> #include<cstring> using namespace std; #define N 36001 #define M 320001 const int inf=500; int mp[21][21]; int n,m,cnt; char s[21]; int sum[401],tim[N][81],wh[N][81]; int sum_door,doorx[81],doory[81],id_door[21][21]; int dis[21][21]; int dx[4]={-1,0,1,0}; int dy[4]={0,1,0,-1}; int tot; int front[N],nxt[M<<1],to[M<<1],val[M<<1],from[M<<1]; int lev[N],num[N]; int path[N]; int cur[N]; int src,decc; struct node { int x,y; }nw,nt; bool inmap(int x,int y) { if(x<=0 || x>n || y<=0 || y>m) return false; return mp[x][y]; } void prebfs(int x,int y) { queue<node>q; nw.x=x; nw.y=y; q.push(nw); while(!q.empty()) { nw=q.front(); q.pop(); for(int i=0;i<4;++i) { nt.x=nw.x+dx[i]; nt.y=nw.y+dy[i]; if(!inmap(nt.x,nt.y)) continue; if(!dis[nt.x][nt.y]) { dis[nt.x][nt.y]=dis[nw.x][nw.y]+1; if(mp[nt.x][nt.y]==2) continue; q.push(nt); } } } } void add(int u,int v,int w) { to[++tot]=v; nxt[tot]=front[u]; front[u]=tot; from[tot]=u; val[tot]=w; to[++tot]=u; nxt[tot]=front[v]; front[v]=tot; from[tot]=v; val[tot]=0; } void rebuild(int mid) { tot=1; memset(front,0,sizeof(front)); decc=mid*sum_door+cnt+1; for(int i=1;i<=cnt;++i) add(src,i,1); for(int i=1;i<=sum_door;++i) for(int j=1;j<=mid;++j) { add((i-1)*mid+j+cnt,decc,1); if(j!=mid) add((i-1)*mid+j+cnt,(i-1)*mid+j+cnt+1,inf); } for(int i=1;i<=cnt;++i) for(int j=1;j<=sum[i];++j) if(tim[i][j]<=mid) add(i,(wh[i][j]-1)*mid+tim[i][j]+cnt,1); } bool bfs() { queue<int>q; for(int i=src;i<=decc;++i) lev[i]=decc; q.push(decc); lev[decc]=0; int now,t; while(!q.empty()) { now=q.front(); q.pop(); for(int i=front[now];i;i=nxt[i]) { t=to[i]; if(lev[t]==decc && val[i^1]) { lev[t]=lev[now]+1; q.push(t); } } } return lev[src]!=decc; } int augment() { int now=decc,flow=inf; int i; while(now!=src) { i=path[now]; flow=min(flow,val[i]); now=from[i]; } now=decc; while(now!=src) { i=path[now]; val[i]-=flow; val[i^1]+=flow; now=from[i]; } return flow; } int isap() { int flow=0; if(!bfs()) return 0; memset(num,0,sizeof(num)); for(int i=src;i<=decc;++i) num[lev[i]]++,cur[i]=front[i]; int now=src,t; while(lev[src]<=decc) { if(now==decc) { flow+=augment(); now=src; } bool advanced=false; for(int i=cur[now];i;i=nxt[i]) { t=to[i]; if(lev[t]==lev[now]-1 && val[i]) { advanced=true; path[t]=i; cur[now]=i; now=t; break; } } if(!advanced) { int mi=decc; for(int i=front[now];i;i=nxt[i]) if(val[i]) mi=min(mi,lev[to[i]]); if(!--num[lev[now]]) break; num[lev[now]=mi+1]++; cur[now]=front[now]; if(now!=src) now=from[path[now]]; } } return flow; } bool check(int mid) { rebuild(mid); return isap()==cnt; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;++i) { scanf("%s",s+1); for(int j=1;j<=m;++j) { if(s[j]==‘.‘) mp[i][j]=1; else if(s[j]==‘D‘) { sum_door++; doorx[sum_door]=i; doory[sum_door]=j; mp[i][j]=2; id_door[i][j]=sum_door; } } } for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) { if(mp[i][j]!=1) continue; cnt++; memset(dis,0,sizeof(dis)); prebfs(i,j); for(int i=1;i<=sum_door;++i) { if(dis[doorx[i]][doory[i]]) { tim[cnt][++sum[cnt]]=dis[doorx[i]][doory[i]]; wh[cnt][sum[cnt]]=id_door[doorx[i]][doory[i]]; } } } int l=0,r=450,mid,ans=-1; while(l<=r) { mid=l+r>>1; if(check(mid)) ans=mid,r=mid-1; else l=mid+1; } if(ans==-1) printf("impossible"); else printf("%d",ans); return 0; }
1189: [HNOI2007]紧急疏散evacuate
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 3362 Solved: 994
[Submit][Status][Discuss]
Description
发生了火警,所有人员需要紧急疏散!假设每个房间是一个N M的矩形区域。每个格子如果是‘.‘,那么表示这是一块空地;如果是‘X‘,那么表示这是一面墙,如果是‘D‘,那么表示这是一扇门,人们可以从这儿撤出房间。已知门一定在房间的边界上,并且边界上不会有空地。最初,每块空地上都有一个人,在疏散的时候,每一秒钟每个人都可以向上下左右四个方向移动一格,当然他也可以站着不动。疏散开始后,每块空地上就没有人数限制了(也就是说每块空地可以同时站无数个人)。但是,由于门很窄,每一秒钟只能有一个人移动到门的位置,一旦移动到门的位置,就表示他已经安全撤离了。现在的问题是:如果希望所有的人安全撤离,最短需要多少时间?或者告知根本不可能。
Input
输入文件第一行是由空格隔开的一对正整数N与M,3<=N <=20,3<=M<=20,以下N行M列描述一个N M的矩阵。其中的元素可为字符‘.‘、‘X‘和‘D‘,且字符间无空格。
Output
只有一个整数K,表示让所有人安全撤离的最短时间,如果不可能撤离,那么输出‘impossible‘(不包括引号)。
Sample Input
XXXXX
X...D
XX.XX
X..XX
XXDXX