bzoj1189: [HNOI2007]紧急疏散evacuate

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1189

思路:一种简单的网络流建图:

预处理两点间距离

从S向每个空地连1的边,每个空地向它在二分的时间内能到的出口连边,出口在向汇连T的边

这也是很多题解的做法

但这是错的...

当很多人距离门很远时,他们就可能在时间快到时堆在门口出不去,这种建图就忽略了这一点


所以我们要拆点

还是二分最大时间T,从S向每个空地连1的边

把每个门拆成T个点,表示每个时间,由每个点向汇连容量为1的边,表示每个时间只能出去一个人

然后由时间早的点向后一个时间的点连边,表示可以在门口等待到下一个时间出去

每个空地向它最早到达这个门的时间所代表的点连1的边

判断最大流是否等于空地数即可

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
const int dx[]={1,-1,0,0};
const int dy[]={0,0,1,-1};
const int maxn=40010,maxm=1000010,inf=(int)1e9;
using namespace std;
int n,m,dis[510][510],map[25][25],dor[510],dcnt,blk[510],bcnt,head,tail;char ch[25][25];bool bo[25][25];
int pre[maxm],now[maxn],son[maxm],val[maxm],tot,di[maxn],S=maxn-2,T=maxn-1,maxt=80;
struct poi{int x,y;}q[510];
int id(int x,int y){return (x-1)*m+y;}

void bfs(int sx,int sy){
	int st=id(sx,sy);
	memset(dis[st],63,sizeof(dis[st]));
	memset(bo,0,sizeof(bo));
	q[tail=1]=(poi){sx,sy};
	head=0,dis[st][st]=0,bo[sx][sy]=1;
	while (head!=tail){
		if (++head>500) head=1;
		poi a=q[head];
		for (int i=0;i<4;i++){
			int nx=a.x+dx[i],ny=a.y+dy[i];
			
			if (bo[nx][ny]) continue;
			if (map[nx][ny]==1){
				if (++tail>500) tail=1;
				bo[nx][ny]=1,dis[st][id(nx,ny)]=dis[st][id(a.x,a.y)]+1;
				q[tail]=(poi){nx,ny};
			}
			else if (map[nx][ny]==2)
				bo[nx][ny]=1,dis[st][id(nx,ny)]=dis[st][id(a.x,a.y)]+1;
		}
	}
}

void init(){
	for (int i=1;i<=n;i++)
		for (int j=1;j<=m;j++)
			if (map[i][j]==1) bfs(i,j),blk[++bcnt]=id(i,j);
			else if (map[i][j]==2) dor[++dcnt]=id(i,j);
}
struct Tflow{
	int q[maxn+10],head,tail;
	int id(int x,int tim){return (x-1)*maxt+tim+bcnt;}
	void add(int a,int b,int c){pre[++tot]=now[a],now[a]=tot,son[tot]=b,val[tot]=c;}
	void ins(int a,int b,int c){add(a,b,c),add(b,a,0);}
	void clear(){memset(now,0,sizeof(now)),tot=1;}
	bool bfs(){
		memset(di,-1,sizeof(di));
		q[1]=S,di[S]=0,head=0,tail=1;
		while (head!=tail){
			if (++head>maxm) head=1;
			int x=q[head];
			for (int y=now[x];y;y=pre[y])
				if (di[son[y]]==-1&&val[y]){
					if (++tail>maxm) tail=1;
					di[son[y]]=di[x]+1,q[tail]=son[y];
				}
		}
		return di[T]>0;
	}
	int find(int x,int low){
		if (x==T) return low;
		int y,res=0;
		for (y=now[x];y;y=pre[y]){
			if (!val[y]||di[son[y]]!=di[x]+1) continue;
			int tmp=find(son[y],min(low,val[y]));
			val[y]-=tmp,val[y^1]+=tmp,res+=tmp,low-=tmp;
			if (!low) break;
		}
		if (!y) di[x]=-1;
		return res;
	}
	bool dinic(int lim){
		int res=0;clear();
		for (int i=1;i<=bcnt;i++) ins(S,i,1);
		for (int i=1;i<=dcnt;i++) for (int j=1;j<=lim;j++){
			ins(id(i,j),T,1);
			if (j>1) ins(id(i,j-1),id(i,j),inf);
		}
		for (int i=1;i<=bcnt;i++)
			for (int j=1;j<=dcnt;j++){
				int x=blk[i],y=dor[j];
				if (dis[x][y]<=lim) ins(i,id(j,dis[x][y]),1);
			}
		while (bfs()) res+=find(S,inf);
		return res==bcnt;
	}
}Ts;

void work(){
	int l=1,r=n*m,mid=(l+r)>>1,ans=-1;
	while (l<=r){
		if (Ts.dinic(mid)) r=mid-1,ans=mid;
		else l=mid+1;
		mid=(l+r)>>1;
	}
	if (ans==-1) puts("impossible");
	else printf("%d
",ans);
}

int main(){
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++) scanf("%s",ch[i]+1);
	for (int i=1;i<=n;i++)
		for (int j=1;j<=m;j++)
			if (ch[i][j]=='D') map[i][j]=2;
			else if (ch[i][j]=='.') map[i][j]=1;
	init(),work();
	return 0;
}


原文地址:https://www.cnblogs.com/thythy/p/5493465.html