然后二分时间,假设时间为x,将每扇门拆成1到x,x个时间点,表示这扇门有几个时间点是可以出去的。对于一扇门,每个时间点都向后一个时间点建边,表示人在当前时间点到达,可以在下一时间点出去。
先将s连上所有的空地,流量为1,建立每个空地每个门的对应的时间点流量为1的边,表示这个空地的人会再某一时间点到达这扇门。然后每个门流向t,流量为inf。只要最大流为空地的数量,则代表该时间是可以的,继续向下二分。
#include<bits/stdc++.h>
#define CLR(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
int fx[4][2]={{0,1},{0,-1},{1,0},{-1,0}};
int dis[100][30][30],n,m;
char s[30][30];
int ed[1000],g[30][30],cnt,sum,st,t;
const int maxn=40010;
const int inf=0x3f3f3f3f;
int head[maxn],tot=0;
struct edge{
int to,f,Next;
}a[maxn<<1];
void addv(int u,int v,int w){
a[++tot].to=v,a[tot].f=w,a[tot].Next=head[u],head[u]=tot;
a[++tot].to=u,a[tot].f=0,a[tot].Next=head[v],head[v]=tot;
}
inline void bfs(int id){
queue<int >q;
dis[id][ed[id]/m][ed[id]%m]=0;
q.push(ed[id]);
while(!q.empty()){
int u=q.front();
int x=u/m,y=u%m;
q.pop();
for(int i=0;i<4;i++){
int xx=x+fx[i][0],yy=y+fx[i][1];
if(xx<0||xx>=n||yy<0||yy>=m||s[xx][yy]!=‘.‘)continue;
if (dis[id][xx][yy]>dis[id][x][y]+1)
dis[id][xx][yy]=dis[id][x][y]+1,q.push(xx*m+yy);
}
}
}
int deep[maxn];
bool vis[maxn];
inline void find(){
CLR(deep,0);
queue<int >q;
vis[st]=1;
q.push(st);
while(!q.empty()){
int u=q.front();
q.pop();
for(int i=head[u];i!=-1;i=a[i].Next)
{
if(a[i].f && !vis[a[i].to]){
q.push(a[i].to);
deep[a[i].to]=deep[u]+1;
vis[a[i].to]=1;
}
}
}
}
int dfs(int u,int delta){
if(u==t)return delta;
int ret=0;
for(int i=head[u];delta&&i!=-1;i=a[i].Next){
if(a[i].f&&deep[a[i].to]==deep[u]+1){
int dd=dfs(a[i].to,min(a[i].f,delta));
a[i].f-=dd;
a[i^1].f+=dd;
delta-=dd;
ret+=dd;
}
}
return ret;
}
inline bool check(int x){
st=0,t=maxn-2,tot=1;
CLR(head,-1);
for(int i=1;i<=sum;i++)addv(st,i,1);
for(int k=1;k<=cnt;k++)
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
if(s[i][j]==‘.‘&&dis[k][i][j]<=x)
addv(g[i][j],sum+(k-1)*x+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;
addv(tmp+j,t,1);
if (j<x) addv(tmp+j,tmp+j+1,inf);
}
int ret=0;
while(1){
CLR(vis,0);
find();
if(!vis[t]){
return ret==sum;
}
ret+=dfs(st,inf);
}
}
int main(){
cin>>n>>m;
for(int i=0;i<n;i++)
{
scanf("%s",s[i]);
}
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
if(s[i][j]==‘D‘){
ed[++cnt]=i*m+j;
}
if(s[i][j]==‘.‘)g[i][j]=++sum;
}
}
CLR(dis,0x3f);
for(int i=1;i<=cnt;i++)
{
bfs(i);
}
int l=1,r=n*m,mid;
int ans;
if(!check(r)){
puts("impossible");
return 0;
}
while(l<=r){
mid=(l+r)>>1;//printf("l:%d r:%d mid:%d\n",l,r,mid);
if(check(mid)){
ans=mid,r=mid-1;
// printf("ans:%d\n",ans);
}
else l=mid+1;
}
cout<<ans<<endl;
}
发生了火警,所有人员需要紧急疏散!假设每个房间是一个N M的矩形区域。每个格子如果是‘.‘,那么表示这是一
块空地;如果是‘X‘,那么表示这是一面墙,如果是‘D‘,那么表示这是一扇门,人们可以从这儿撤出房间。已知门
一定在房间的边界上,并且边界上不会有空地。最初,每块空地上都有一个人,在疏散的时候,每一秒钟每个人都
可以向上下左右四个方向移动一格,当然他也可以站着不动。疏散开始后,每块空地上就没有人数限制了(也就是
说每块空地可以同时站无数个人)。但是,由于门很窄,每一秒钟只能有一个人移动到门的位置,一旦移动到门的
位置,就表示他已经安全撤离了。现在的问题是:如果希望所有的人安全撤离,最短需要多少时间?或者告知根本
不可能。
第一行是由空格隔开的一对正整数N与M,3<=N <=20,3<=M<=20,
以下N行M列描述一个N M的矩阵。其中的元素可为字符‘.‘、‘X‘和‘D‘,且字符间无空格。
只有一个整数K,表示让所有人安全撤离的最短时间,
如果不可能撤离,那么输出‘impossible‘(不包括引号)。