[BZOJ1189][HNOI2007]紧急疏散evacuate
sol
显然时间是二分的对吧。
我们二分一个时间,假设为\(mid\),那么就要把每扇门拆成\(mid\)个点,各自向汇点连容量为1的边,表示每一扇门在一个单位时间里可以让一个人逃出。
先\(BFS\)预处理出每个人到每扇门的距离,然后如果这个人在\(mid\)时间内可以到达这扇门就向这扇门对应的时间点连一条容量为1的边。
源点向每个人连容量为1的边,然后二分判断最大流是否等于总人数。
但是!
如果同一时间有多个人到达了同一扇门,然后其中一个人发挥谦让精神让另一个人先走了,自己在门这里等了一个单位时间才走的,那我们怎么处理呢?
很简单。从每扇门对应时间\(t\)的点向\(t+1\)的点连一条\(inf\)的边,就可以解决这种问题了。
code
细节。。。还好吧
一些需要注意的点在代码中已经用注释标出
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int N = 40;
const int inf = 1e9;
int n,m,P[N][N],dis[N<<2][N][N],door,people;
char g[N][N];
struct node{int x,y;};
int dx[4]={1,0,-1,0},dy[4]={0,1,0,-1};
queue<int>Q;
queue<node>QQ;
void BFS(int k,int p,int q)//第k扇门,它的位置是(p,q)
{
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
dis[k][i][j]=inf;
dis[k][p][q]=0;QQ.push((node){p,q});
while (!QQ.empty())
{
int x=QQ.front().x,y=QQ.front().y;QQ.pop();
for (int d=0;d<4;d++)
{
int i=x+dx[d],j=y+dy[d];
if (i<1||i>n||j<1||j>m||g[i][j]!='.') continue;//这里如果写成" g[i][j]=='X' "的话在BZOJ上会WA(但是洛谷可以AC)
if (dis[k][i][j]>dis[k][x][y]+1)
dis[k][i][j]=dis[k][x][y]+1,QQ.push((node){i,j});
}
}
}
struct edge{int to,next,w;}a[N*N*N*N];
int S,T,head[N*N*N],cnt,dep[N*N*N],cur[N*N*N];
void link(int u,int v,int w)
{
a[++cnt]=(edge){v,head[u],w};
head[u]=cnt;
a[++cnt]=(edge){u,head[v],0};
head[v]=cnt;
}
bool bfs()
{
memset(dep,0,sizeof(dep));
dep[S]=1;Q.push(S);
while (!Q.empty())
{
int u=Q.front();Q.pop();
for (int e=head[u];e;e=a[e].next)
if (a[e].w&&!dep[a[e].to])
dep[a[e].to]=dep[u]+1,Q.push(a[e].to);
}
return dep[T];
}
int dfs(int u,int flow)
{
if (u==T)
return flow;
for (int &e=cur[u];e;e=a[e].next)
if (a[e].w&&dep[a[e].to]==dep[u]+1)
{
int temp=dfs(a[e].to,min(flow,a[e].w));
if (temp) {a[e].w-=temp;a[e^1].w+=temp;return temp;}
}
return 0;
}
int Dinic()
{
int res=0;
while (bfs())
{
for (int i=T;i;i--) cur[i]=head[i];
while (int temp=dfs(S,inf)) res+=temp;
}
return res;
}
int check(int mid)
{
S=door*mid+people+1;T=S+1;
memset(head,0,sizeof(head));cnt=1;
for (int k=1;k<=door;k++)
for (int t=1;t<=mid;t++)
{
link((k-1)*mid+t,T,1);
if (t<mid) link((k-1)*mid+t,(k-1)*mid+t+1,inf);
}
for (int i=1;i<=people;i++)
link(S,door*mid+i,1);
for (int k=1;k<=door;k++)
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
if (g[i][j]=='.'&&dis[k][i][j]<=mid)//这里一定要写" g[i][j]=='.' ",不然就会把自己这扇门的那个位置也算进去(就这样洛谷上还跑90分?)
link(door*mid+P[i][j],(k-1)*mid+dis[k][i][j],1);
return Dinic();
}
int main()
{
scanf("%d %d",&n,&m);
for (int i=1;i<=n;i++)
scanf("%s",g[i]+1);
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
if (g[i][j]=='D')
BFS(++door,i,j);
else if (g[i][j]=='.')
P[i][j]=++people;
int l=0,r=people+1;
while (l<r)
{
int mid=l+r>>1;
if (check(mid)==people) r=mid;
else l=mid+1;
}
if (l==people+1) puts("impossible");
else printf("%d\n",l);
return 0;
}