bzoj1189 [HNOI2007]紧急疏散EVACUATE spfa+网络流+二分

这个题是非常暴力的匹配问题。

首先最好想的思路是给每个人分门的决策,

每个人到每个门的距离直接暴力最短路即可

但不能算出一个门被多个人经过的情况

所以就有了暴力的想法,再给每个人、对每一个门分配一个时间。

这样的话时间的范围就必须确定,不然就是动态借边的网络流

由于时间满足二分的条件,所以二分+网络流检验即可


码:

#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
#include<queue>
using namespace std;
#define inf 1000000009
int tot,ans,l,r,s,t,n,d[3000000],m,i,j,k,hou[3000000],v[3000000],zhong[3000000],yuan[3000000],xia[3000000];
char tu[40][40];
bool vis[3000000];
vector<int>D,dian,jl[3000000];
queue<int>q;
void jian(int a,int b,int c)
{
	++tot;hou[tot]=yuan[a];yuan[a]=tot;v[tot]=c;zhong[tot]=b;	
}
void jia(int a,int b,int c)
{
	jian(a,b,c);
	jian(b,a,0);	
}
void spfa(int o)
{int i;
	memset(d,0x7f,sizeof(d));
	q.push(o);
	d[o]=0;
	while(!q.empty())
	{
		int st=q.front();
		q.pop();
	vis[st]=0;
	for(i=yuan[st];i!=-1;i=hou[i])
	{
		int nd=zhong[i];
	if(d[nd]>d[st]+1)
	{
	d[nd]=d[st]+1;
	if(vis[nd]==0)
	{
	vis[nd]=1;
	q.push(nd);		
	}	
	}		
	}	
	}	
}
bool bfs()
{
	int i;
	memset(d,0x7f,sizeof(d));
	for(i=1;i<=t;i++)
	xia[i]=yuan[i];
	q.push(s);
	d[s]=0;
	while(!q.empty())
	{
		int st=q.front();
		q.pop();
		for(i=xia[st];i!=-1;i=hou[i])
		{
			int nd=zhong[i];
			if(!v[i]||d[nd]<inf)continue;
			d[nd]=d[st]+1;
			q.push(nd);	
		}		
	}
	return d[t]<inf;
}
int dfs(int o,int t,int limit)
{
	if(o==t||limit==0)return limit;
	int i,flow=0,f;
	for(i=xia[o];i!=-1;i=hou[i])
	{
		xia[o]=i;
		int nd=zhong[i];
		if(d[nd]==d[o]+1&&(f=dfs(nd,t,min(limit,v[i]))))
		{
			flow+=f;
			limit-=f;
			v[i]-=f;
			v[i^1]+=f;			
		if(limit==0)break;
		}	
	}
	return flow;
}
int dinic()
{
	int daan=0;
	while(bfs())
	{
		daan+=dfs(s,t,inf);
	}	
	return daan;
}
int main()
{	
  memset(yuan,-1,sizeof(yuan));
	scanf("%d%d",&n,&m);
	for(i=1;i<=n;i++)
	for(j=1;j<=m;j++)
	{
		scanf("%c",&tu[i][j]);
		while(tu[i][j]!='X'&&tu[i][j]!='.'&&tu[i][j]!='D')scanf("%c",&tu[i][j]);	
	if(tu[i][j]=='D')D.push_back((i-1)*m+j);
	else if(tu[i][j]=='.')dian.push_back((i-1)*m+j);
	}
for(i=2;i<n;i++)
for(j=2;j<m;j++)
{
	if(tu[i][j]=='X')continue;
	if(tu[i-1][j]!='X')jian((i-1)*m+j,(i-2)*m+j,1);
	if(tu[i+1][j]!='X')jian((i-1)*m+j,(i)*m+j,1);
	if(tu[i][j+1]!='X')jian((i-1)*m+j,(i-1)*m+j+1,1);	
	if(tu[i][j-1]!='X')jian((i-1)*m+j,(i-1)*m+j-1,1);
}
r=400;
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
{
	if(tu[i][j]=='X')continue;
	spfa((i-1)*m+j);
	int lin=999;	
	for(k=0;k<D.size();k++)
	{
		int xy=D[k];
		jl[(i-1)*m+j].push_back(d[xy]);		
	lin=min(lin,d[xy]);
	}	
//	cout<<(i-1)*m+j<<" : "<<jl[(i-1)*m+j].size()<<endl;
	r=max(lin,r);
}
if(r>400)
{
	printf("impossible");
	return 0;	
}
s=n*m+400*400+1;
t=s+1;
l=0;
while(l<r)
{
int mid=(l+r)>>1;
tot=-1;//cout<<mid;
memset(yuan,-1,sizeof(yuan));
for(i=0;i<dian.size();i++)
{
	int st=dian[i];
	jia(s,st,1);
	for(j=0;j<D.size();j++)
	{	//cout<<st<<" "<<jl[st].size()<<endl;
		int nd=jl[st][j];
		for(k=nd;k<=mid;k++)
		{
		jia(st,n*m+(400*j)+k,1);	
		}
    }
}
	for(i=0;i<D.size();i++)
	for(j=1;j<=mid;j++)jia(n*m+400*i+j,t,1);
if(dinic()==dian.size())
{
ans=mid;
r=mid;	
}else l=mid+1;	
}
	printf("%d",ans);
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值