1689: [HNOI2007]紧急疏散evacuate
时间限制: 1 Sec
内存限制: 128 MB
题目描述
发生了火警,所有人员需要紧急疏散!假设每个房间是一个N M的矩形区域。每个格子如果是".",那么表示这是一块空地;如果是"X",那么表示这是一面墙,如果是"D",那么表示这是一扇门,人们可以从这儿撤出房间。已知门一定在房间的边界上,并且边界上不会有空地。最初,每块空地上都有一个人,在疏散的时候,每一秒钟每个人都可以向上下左右四个方向移动一格,当然他也可以站着不动。疏散开始后,每块空地上就没有人数限制了(也就是说每块空地可以同时站无数个人)。但是,由于门很窄,每一秒钟只能有一个人移动到门的位置,一旦移动到门的位置,就表示他已经安全撤离了。现在的问题是:如果希望所有的人安全撤离,最短需要多少时间?或者告知根本不可能。
输入
输入文件第一行是由空格隔开的一对正整数N与M,3<=N <=20,3<=M<=20,以下N行M列描述一个N M的矩阵。其中的元素可为字符"."、"X"和"D",且字符间无空格。
输出
只有一个整数K,表示让所有人安全撤离的最短时间,如果不可能撤离,那么输出"impossible"(不包括引号)。
样例输入
5 5
XXXXX
X...D
XX.XX
X..XX
XXDXX
样例输出
3
因为每一秒门只能出一个人,所以人可能堵住。因此要把门按时间拆点,
而答案为二分时间,只要求出空地到每扇门的最短路(最短时间),然后把此空地与门连接(时间为最短路径到二分的时间),边权为一。
注意拆出的新点标号别重复。。身败名裂。。
#include
#include
#include
#include
#include
#define inf 100000000
using namespace std;
int n,m,a[404][404],cnt=0,door[404],peo[404];
int tim[404][704],adj[5000000],s=0,dep[5000000],e;
int S=0,T;
char tu[25][25];
struct node
{
int v,l,next;
} lu[5000100];
void add(int u,int v,int l)
{lu[++e].v=v;lu[e].next=adj[u];adj[u]=e;lu[e].l=l;}
int bfs()
{
memset(dep,0,sizeof(dep));
queue q;
dep[S]=1;
q.push(S);
while(!q.empty())
{
int x=q.front();q.pop();
for(int i=adj[x];i!=-1;i=lu[i].next)
{
int to=lu[i].v;
if(!dep[to]&&lu[i].l)
{
dep[to]=dep[x]+1;
if(to==T)
return 1;
q.push(to);
}
}
}
return 0;
}
int dfs(int x,int fw){
if(x==T) return fw;
int tmp=fw,k;
for(int i=adj[x];i!=-1;i=lu[i].next){
int v=lu[i].v;
if(lu[i].l && tmp && dep[v]==dep[x]+1){
k=dfs(v,min(tmp,lu[i].l));
if(!k){
dep[v]=0;
continue;
}
lu[i].l-=k; lu[i^1].l+=k; tmp-=k;
}
}
return fw-tmp;
}
int check(int len)
{
memset(adj,-1,sizeof(adj));
e=0;
int sum=cnt;
for(int i=1;i<=cnt;i++)
if(door[i])
for(int j=1;j<=len;j++)
tim[i][j]=(i-1)*len+cnt;
T=300000;
for(int i=1;i<=cnt;i++)
if(peo[i])
{
add(S,i,1),add(i,S,0);
for(int j=1;j<=cnt;j++)
if(door[j])
{
int k=a[i][j];
for(k=a[i][j];k<=len;k++)
add(i,tim[j][k],1),add(tim[j][k],i,0);
}
}
for(int i=1;i<=cnt;i++)
if(door[i])
{
for(int j=1;j<=len;j++)
add(tim[i][j],T,1),add(T,tim[i][j],0);
}
int ans=0,tt;
while(bfs())
ans+=dfs(S,inf);
if(ans>=s)return 1;
return 0;
}
int main()
{
// freopen("data.in","r",stdin);
// freopen("data.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%s",tu[i]+1);
memset(a,30,sizeof(a));
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
cnt++;
if(tu[i][j]=='.')
{
s++;
peo[cnt]=1;
if(i!=1&&(tu[i-1][j]=='.'||tu[i-1][j]=='D'))a[cnt][cnt-m]=1,a[cnt-m][cnt]=1;
if(i!=n&&(tu[i+1][j]=='.'||tu[i+1][j]=='D'))a[cnt][cnt+m]=1,a[cnt+m][cnt]=1;
if(j!=1&&(tu[i][j-1]=='.'||tu[i][j-1]=='D'))a[cnt][cnt-1]=1,a[cnt-1][cnt]=1;
if(j!=m&&(tu[i][j+1]=='.'||tu[i][j+1]=='D'))a[cnt][cnt+1]=1,a[cnt+1][cnt]=1;
}
if(tu[i][j]=='D')
{
door[cnt]=1;
}
}
for(int k=1;k<=cnt;k++)
for(int i=1;i<=cnt;i++)
for(int j=1;j<=cnt;j++)
if(a[i][j]>a[i][k]+a[k][j])
a[i][j]=a[i][k]+a[k][j];
for(int i=1;i<=cnt;i++)
if(peo[i])
{
int p=1;
for(int j=1;j<=cnt;j++)
if(door[j]&&a[i][j]<1000)
{p=0;break;}
if(p==1)
{
printf("impossible");
exit(0);
}
}
int l=0,r=600,mid,ans=600;
while(l<=r)
{
mid=(l+r)/2;
if(check(mid))
r=mid-1,ans=mid;
else
l=mid+1;
}
printf("%d",ans);
}