1189: [HNOI2007]紧急疏散evacuate
Time Limit: 10 Sec
Memory Limit: 128 MB
Submit: 1817
Solved: 605
[ 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
5 5
XXXXX
X...D
XX.XX
X..XX
XXDXX
Sample Output
3
HINT
2015.1.12新加数据一组,鸣谢1756500824
C++语言请用scanf("%s",s)读入!
Source
[ Submit][ Status][ Discuss]
题解:二分答案+最大流
刚开始想用费用流,但是发现费用流没法处理两个人在同一时间到达,一个人需要再等一秒钟的情况,于是改成了最大流。
先预处理出每个'.'到每个门的最短距离,然后二分一个最短时间mid
从源点向所有的'.'点连一条容量为1的边,因为每个位置最初有一个人。
然后把每个门拆成mid个时间点,从这mid个点在向这个门所代表的点连一条容量mid-x+1的边(x表示到达该门的时间,为什么呢不妨想一下,如果有两个人在2这个时间点到达,当前的总时限为2,因为有一个人一定需要等待,即需要在第3秒出门,所以加了这个容量限制,就能保证在时间内不会有人多出去),然后再从门所代表的点向汇点连一条容量为mid的边(因为在mid时间内,出门的人数不可能超过mid)。
如果某个人的某个门的距离<=mid,那么就从这个人向门拆成的点代表的当前所需时间的点连边。
大体方式如下图:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define N 100000
#define inf 1000000000
using namespace std;
int dis[N],next[N],point[N],v[N],remain[N];
int n,m,k,tot=-1,head,tail,maxflow,mincost,mid,cnt;
int last[N],can[N],a[100][100],f[33][33][33][33],vis[33][33];
int mark[33][33],sz,ch[432][3],deep[N],cur[N],num[N];
int x[10]={0,1,0,-1},y[10]={1,0,-1,0};
int q1[N],q2[N];
char s[100];
void add(int x,int y,int z)
{
tot++; next[tot]=point[x]; point[x]=tot; v[tot]=y; remain[tot]=z;
tot++; next[tot]=point[y]; point[y]=tot; v[tot]=x; remain[tot]=0;
}
int addflow(int s,int t)
{
int now=t; int ans=inf;
while (now!=s)
{
ans=min(ans,remain[last[now]]);
now=v[last[now]^1];
}
now=t;
while (now!=s)
{
remain[last[now]]-=ans;
remain[last[now]^1]+=ans;
now=v[last[now]^1];
}
return ans;
}
void bfs(int s,int t)
{
for (int i=s;i<=t;i++)
deep[i]=t;
deep[t]=0;
queue<int> p; p.push(t);
while (!p.empty())
{
int now=p.front(); p.pop();
for (int i=point[now];i!=-1;i=next[i])
if (deep[v[i]]==t&&remain[i^1])
deep[v[i]]=deep[now]+1,p.push(v[i]);
}
}
bool isap(int s,int t)
{
bfs(s,t);
for (int i=s;i<=t;i++)
cur[i]=point[i];
for (int i=s;i<=t;i++)
num[deep[i]]++;
int now=s; int ans=0;
while (deep[s]<t)
{
if (now==t)
{
ans+=addflow(s,t);
now=s;
}
bool f=false;
for (int i=cur[now];i!=-1;i=next[i])
if (deep[v[i]]+1==deep[now]&&remain[i])
{
last[v[i]]=i;
f=true;
cur[now]=i;
now=v[i];
break;
}
if(!f)
{
int minn=t;
for (int i=point[now];i!=-1;i=next[i])
if (remain[i]) minn=min(deep[v[i]],minn);
if (!--num[deep[now]]) break;
deep[now]=minn+1;
num[deep[now]]++;
cur[now]=point[now];
if (now!=s)
now=v[last[now]^1];
}
}
return ans==cnt;
}
void Bfs(int xl,int yl)
{
memset(vis,0,sizeof(vis));
int l=0; int r=1;
q1[r]=xl; q2[r]=yl;
f[xl][yl][xl][yl]=0;
vis[xl][yl]=1;
while (l!=r)
{
l++;
int nowx=q1[l]; int nowy=q2[l]; int t=f[nowx][nowy][xl][yl];
for (int i=0;i<4;i++)
{
int xx=nowx+x[i]; int yy=nowy+y[i];
if (xx>0&&yy>0&&xx<=n&&yy<=m&&a[xx][yy]==1&&!vis[xx][yy])
{
f[xx][yy][xl][yl]=t+1;
vis[xx][yy]=1;
r++; q1[r]=xx; q2[r]=yy;
}
}
}
}
int build()
{
tot=-1;
memset(point,-1,sizeof(point));
memset(next,-1,sizeof(next));
int num=cnt+1+sz*mid;
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
{
if (a[i][j]==1)
{
add(1,mark[i][j]+1,1);
for (int k=1;k<=sz;k++)
{
int xx=ch[k][0]; int yy=ch[k][1];
if (f[i][j][xx][yy]&&f[i][j][xx][yy]<=mid)
{
int t=cnt+1+(mark[xx][yy]-1)*mid+f[i][j][xx][yy];
add(mark[i][j]+1,t,1);
}
}
}
if (a[i][j]==2)
{
for (int k=1;k<=mid;k++)
{
int t=cnt+1+(mark[i][j]-1)*mid+k;
add(t,num+mark[i][j],mid-k+1);
}
add(num+mark[i][j],num+sz+1,mid);
}
}
return num+sz+1;
}
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]=='.') a[i][j]=1,cnt++,mark[i][j]=cnt;
else if (s[j]=='D') a[i][j]=2;
else if (s[j]=='X') a[i][j]=0;
}
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
if (a[i][j]==2)
{
Bfs(i,j),mark[i][j]=++sz,ch[sz][0]=i,ch[sz][1]=j;
}
head=1; tail=m*n;
int ans=0;
while (head<=tail)
{
mid=(head+tail)/2;
int t=build();
if (isap(1,t))
ans=mid,tail=mid-1;
else
head=mid+1;
}
if (!ans)
printf("impossible\n");
else
printf("%d\n",ans);
}