题目链接
http://www.lydsy.com/JudgeOnline/problem.php?id=1189
思路
非常好的一道网络流的题。。。
首先我们对整个地图,以每道门为终点分别都BFS一遍,得到每个空地到每个门的最短距离,然后二分所有人都疏散出来的最少时间。假设我们现在已知这个时间,判断这个疏散所花费的时间是否是合法的。源点向所有空地连容量为1的边,表示初始时每个空地上都有人,然后所有门向汇连容量为疏散时间的边,表示在限制时间内每个门能够疏散出来的人的数量。然后对于每个空地a和门b,若a到b的时间小于等于疏散时间,就从a连一条容量为1的边到b,表示初始在a空地的人可以跑到门b处疏散。判断这个网络流是否满流,若满流则当前二分出的疏散花费时间显然是合法的
代码
马上就要省选了,我还在犯一堆SB错误,本来这个题完全可以一遍写完AC的,结果因为SB错误卡了很长时间,怎么办啊。。。
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#define MAXV 10010
#define MAXE 100010
#define INF 0x3f3f3f3f
using namespace std;
//BFS部分
int xx[]={1,-1,0,0},yy[]={0,0,1,-1};
int n,m,totdoor=0; //totdoor=门的个数
int totman=0; //人的个数
bool vis[25][25];
int dist[410][25][25]; //dist[id][x][y]=第id个人到(x,y)的最短距离
char map[25][25];
struct Point //保存每个门的坐标
{
int x,y;
}doors[410];
struct Info
{
int x,y,step; //当前点在(x,y),步数为step
Info(){}
Info(int _x,int _y,int _step):x(_x),y(_y),step(_step){}
}q[1100];
bool inMap(int x,int y)
{
if(x<0||x>n||y<0||y>m) return false;
return true;
}
void BFS(int id) //对第id个人进行广搜
{
memset(vis,0,sizeof(vis));
int h=0,t=1;
q[h]=Info(doors[id].x,doors[id].y,0);
vis[q[h].x][q[h].y]=true;
while(h<t)
{
Info now=q[h++];
int nowx=now.x,nowy=now.y;
for(int dir=0;dir<4;dir++)
{
int newx=nowx+xx[dir],newy=nowy+yy[dir];
if(!inMap(newx,newy)) continue;
if(map[newx][newy]=='.'&&!vis[newx][newy])
{
dist[id][newx][newy]=now.step+1;
vis[newx][newy]=true;
q[t++]=Info(newx,newy,now.step+1);
}
}
}
}
//Dinic最大流
int S=MAXV-2,T=MAXV-1;
struct edge
{
int u,v,cap,next;
}edges[MAXE];
int head[MAXV],nCount=1;
int layer[MAXV];
void AddEdge(int U,int V,int C)
{
edges[++nCount].u=U;
edges[nCount].v=V;
edges[nCount].cap=C;
edges[nCount].next=head[U];
head[U]=nCount;
}
void add(int U,int V,int C)
{
AddEdge(U,V,C);
AddEdge(V,U,0);
}
int que[MAXE];
bool CountLayer()
{
memset(layer,-1,sizeof(layer));
int h=0,t=1;
layer[S]=1;
que[h]=S;
while(h<t)
{
int u=que[h++];
for(int p=head[u];p!=-1;p=edges[p].next)
{
int v=edges[p].v;
if(edges[p].cap&&layer[v]==-1)
{
layer[v]=layer[u]+1;
que[t++]=v;
}
}
}
return layer[T]!=-1;
}
int DFS(int u,int flow)
{
if(u==T) return flow;
int used=0;
for(int p=head[u];p!=-1;p=edges[p].next)
{
int v=edges[p].v;
if(edges[p].cap&&layer[v]==layer[u]+1)
{
int tmp=DFS(v,min(flow-used,edges[p].cap));
used+=tmp;
edges[p].cap-=tmp;
edges[p^1].cap+=tmp;
if(used==flow) return used;
}
}
if(!used) layer[u]=-1;
return used;
}
int Dinic()
{
int maxflow=0;
while(CountLayer())
maxflow+=DFS(S,INF);
return maxflow;
}
inline int calc(int x,int y)
{
return (x-1)*m+y;
}
void BuildGraph(int costtime) //根据总花费时间建图
{
memset(head,-1,sizeof(head));
nCount=1;
for(int i=1;i<=n;i++) //源点向所有空地连容量为1的边
for(int j=1;j<=m;j++)
if(map[i][j]=='.')
add(S,calc(i,j),1);
for(int i=1;i<=totdoor;i++) //所有门向汇点连容量为限制总时间的边
{
add(n*m+i+1,T,costtime);
for(int j=1;j<=n;j++)
for(int k=1;k<=m;k++)
if(dist[i][j][k]<=costtime) //所有到该门足够近的出发点向该门连边
add(calc(j,k),n*m+i+1,1);
}
}
bool check(int costtime) //检查在当前限定的最大时间内能否疏散所有人
{
BuildGraph(costtime);
return Dinic()==totman;
}
int main()
{
int ans=-1;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%s",map[i]+1);
for(int j=1;j<=m;j++)
{
if(map[i][j]=='.') totman++;
if(map[i][j]=='D')
{
doors[++totdoor].x=i;
doors[totdoor].y=j;
}
}
}
memset(dist,INF,sizeof(dist)); //!!!!!
for(int i=1;i<=totdoor;i++) BFS(i);
int lowerBound=0,upperBound=INF;
while(lowerBound<=upperBound)
{
int mid=(lowerBound+upperBound)>>1;
if(check(mid))
{
ans=mid;
upperBound=mid-1;
}
else lowerBound=mid+1;
}
if(ans!=-1) printf("%d\n",ans);
else printf("impossible\n"); //!!!!!
return 0;
}