BZOJ 1189: [HNOI2007]紧急疏散evacuate

9 篇文章 0 订阅
5 篇文章 0 订阅
首先对每一个块空地进行bfs,预处理出每一块空地距离到达每一扇门的距离;然后二分枚举答案,通过跑最大流来验证。
注意bfs时的初始化与省略小小的剪枝;
注意数组的大小。
#include <bits/stdc++.h>
using namespace std;
const int N=21;
const int dx[4]={0,-1,0,1},dy[4]={-1,0,1,0};
int n,m,tot,size1,size2;
int id[N][N],d[N*N][N*N],a[N*N],b[N*N];
bool vis[N][N];
char str[N][N];

struct node{int x,y,z;};
queue<node>q;

inline void init(int x,int y)
{
	memset(vis,false,sizeof(vis));
	int now=id[x][y];
	q.push((node){x,y,0}); vis[x][y]=true; d[now][id[x][y]]=0;
	while (q.size())
	{
		node u=q.front(); q.pop();
		for (register int i=0; i<4; ++i)
		{
			int xx=u.x+dx[i],yy=u.y+dy[i];
			if (xx<1 || xx>n || yy<1 || yy>m) continue;
			if (str[xx][yy]=='X' || vis[xx][yy]) continue;
			vis[xx][yy]=true;
			if (str[xx][yy]=='.') q.push((node){xx,yy,u.z+1});
			else d[now][id[xx][yy]]=u.z+1;
		}
	}
	//洛谷过后发现BZOJ错了一个点,附上该点数据
	//原来,当一个门后面存在另一个门,且这个门是另一个门联通外界的门户时, 
	//(即另一个门想出去只能通过这个门或其他门,而不能通过'.') 
	//这个门后面的另一个个门,是不会用到的。 
	//可以这样证明:如果一个人到达了前面的门不出去,那么他还是要走至少一步才能到达后面的另一个门
	//而由于一个门在一时间内只能存在一个人,无论出不出去,所以他后面的人还是只能和原来一样等待
	//可能他后面的人从他不出去的这个门出去的同时,他也能从另一个门出去 
	//但是如果他从另一个门出去的话,最好的结果最多是和他从前一个门直接出去的结果一样优 
	//所以我们对于那种:“最好结果”只能和前面的门一样优的门,就不去考虑了。 
	//现在有同学说:即使考虑了也没有关系,反正我们跑的是最大流啊,哪怕是从另一个门出去也能得到最优答案。 
	//确实是能够得到最优答案,但是这对于我们建图就有一些影响了
	//观察下面的这组数据:
	/*
	5 4
	XDXX
	DXXX
	D..X
	X..X
	XXXX
	*/ 
	//如果我们要算(2,1)的'D'与'.'的距离的话,就显得比较麻烦了。
	//一开始我觉得,可以用这种写法 			
	/*if (str[xx][yy]=='.') q.push((node){xx,yy,u.z+1});
	else
	{
		d[now][id[xx][yy]]=u.z+1;
		q.push((node){xx,yy,u.z+2});	
	}*/
	//但是这种写法又会被类似以下的样例叉掉
	/*
	5 4
	XXXX
	D.XX
	D..X
	X..X
	XXXX		
	*/ 
	//而如果一定要把没用的点放进去的话,跑bfs就要改为跑dij了,多加一支log
	// 有没有觉得很愚蠢啊,为了一个可以省略的东西多加一支log
	//所以那种方法我就不写了。 
}

const int M=2e5+5,MM=1e7+5;
//这里简单说一下为什么MM为1e7+5,因为最大的数据跑出来的cnt为8686601
//所以就开到1e7了 
int s,t;
int dep[M]; 
int cnt,head[M];
struct edge{int next,to,w;}e[MM];

inline void add(int u,int v,int w)
{
	cnt++;
	e[cnt].next=head[u];
	e[cnt].to=v;
	e[cnt].w=w;
	head[u]=cnt;
}

queue<int>q1;
inline bool bfs()
{
	memset(dep,-1,sizeof(dep));
	dep[s]=0; q1.push(s);
	while (q1.size())
	{
		int u=q1.front(); q1.pop();
		for (register int i=head[u]; i; i=e[i].next)
		if (e[i].w && dep[e[i].to]==-1)
		{
			dep[e[i].to]=dep[u]+1;
			q1.push(e[i].to);
		}
	}
	if (dep[t]==-1) return false;
	else return true;
}

int dfs(int u,int flow)
{
	if (u==t) return flow;
	int remain=flow;
	for (register int i=head[u]; i; i=e[i].next)
	if (e[i].w && dep[u]+1==dep[e[i].to])
	{
		int k=dfs(e[i].to,min(remain,e[i].w));
		if (!k) {dep[e[i].to]=-1; continue;}
		remain-=k; e[i].w-=k; e[i^1].w+=k;
		if (!remain) break;
	}
	return flow-remain;
}

inline int dinic()
{
	int ans=0;
	while (bfs()) ans+=dfs(s,2e9);
	return ans;
}

inline bool jay(int mid)
{
	cnt=1; memset(head,0,sizeof(head));
	s=0; t=size1+size2*mid+1;
	
	for (register int i=1; i<=size1; ++i) add(s,i,1),add(i,s,0);
	for (register int i=1; i<=size1; ++i)
	for (register int j=1; j<=size2; ++j)
	for (register int k=d[a[i]][b[j]]; k<=mid; ++k) add(i,size1+(j-1)*mid+k,1),add(size1+(j-1)*mid+k,i,0);
	for (register int i=size1+1; i<=size1+size2*mid; ++i) add(i,t,1),add(t,i,0);

	if (dinic()>=size1) return true;
	else return false;
}

int main(){
	memset(d,60,sizeof(d)); 
	//本来没有写这个初始化,结果发现洛谷提交70分,原来是因为有一些'D'被封闭掉,根本达到不了 
	scanf("%d%d",&n,&m);
	for (register int i=1; i<=n; ++i) scanf("%s",str[i]+1); 
	for (register int i=1; i<=n; ++i)
	for (register int j=1; j<=m; ++j)
	{
		id[i][j]=++tot;
		if (str[i][j]=='.') a[++size1]=id[i][j];
		if (str[i][j]=='D') b[++size2]=id[i][j];
	}
	for (register int i=1; i<=n; ++i)
	for (register int j=1; j<=m; ++j) if (str[i][j]=='.') init(i,j);
	
	int l,r,mid,ans;
	l=0; r=n*m; ans=-1;
	while (l<=r)
	{
		mid=l+r>>1;
		if (jay(mid)) ans=mid,r=mid-1;
		else l=mid+1;
	}
	if (ans==-1) puts("impossible");
	else printf("%d\n",ans);
return 0;
}
看了评测记录以后发现自己的代码挺慢,后来发现是由于我对每一块空地都做了一次bfs,如果对每一扇门都做一次bfs,可以得到同样的效果,且对于极限数据来讲,门的数量比空地的数量少很多。
所以对门做bfs可以快很多。

在这里插入图片描述

BZOJ上的记录,上面的是对门做bfs,下面的是原来对空地做bfs。
#include <bits/stdc++.h>
using namespace std;
const int N=21;
const int dx[4]={0,-1,0,1},dy[4]={-1,0,1,0};
int n,m,tot,size1,size2;
int id[N][N],d[N*N][N*N],a[N*N],b[N*N];
bool vis[N][N];
char str[N][N];

struct node{int x,y,z;};
queue<node>q;

inline void init(int x,int y)
{
	memset(vis,false,sizeof(vis));
	int now=id[x][y];
	q.push((node){x,y,0}); vis[x][y]=true; d[id[x][y]][now]=0;
	while (q.size())
	{
		node u=q.front(); q.pop();
		for (register int i=0; i<4; ++i)
		{
			int xx=u.x+dx[i],yy=u.y+dy[i];
			if (xx<1 || xx>n || yy<1 || yy>m) continue;
			if (str[xx][yy]!='.' || vis[xx][yy]) continue;
			vis[xx][yy]=true;
			q.push((node){xx,yy,u.z+1});
			d[id[xx][yy]][now]=u.z+1;
		}
	}
}

const int M=2e5+5,MM=1e7+5;
int s,t;
int dep[M]; 
int cnt,head[M];
struct edge{int next,to,w;}e[MM];

inline void add(int u,int v,int w)
{
	cnt++;
	e[cnt].next=head[u];
	e[cnt].to=v;
	e[cnt].w=w;
	head[u]=cnt;
}

queue<int>q1;
inline bool bfs()
{
	memset(dep,-1,sizeof(dep));
	dep[s]=0; q1.push(s);
	while (q1.size())
	{
		int u=q1.front(); q1.pop();
		for (register int i=head[u]; i; i=e[i].next)
		if (e[i].w && dep[e[i].to]==-1)
		{
			dep[e[i].to]=dep[u]+1;
			q1.push(e[i].to);
		}
	}
	if (dep[t]==-1) return false;
	else return true;
}

int dfs(int u,int flow)
{
	if (u==t) return flow;
	int remain=flow;
	for (register int i=head[u]; i; i=e[i].next)
	if (e[i].w && dep[u]+1==dep[e[i].to])
	{
		int k=dfs(e[i].to,min(remain,e[i].w));
		if (!k) {dep[e[i].to]=-1; continue;}
		remain-=k; e[i].w-=k; e[i^1].w+=k;
		if (!remain) break;
	}
	return flow-remain;
}

inline int dinic()
{
	int ans=0;
	while (bfs()) ans+=dfs(s,2e9);
	return ans;
}

inline bool jay(int mid)
{
	cnt=1; memset(head,0,sizeof(head));
	s=0; t=size1+size2*mid+1;
	
	for (register int i=1; i<=size1; ++i) add(s,i,1),add(i,s,0);
	for (register int i=1; i<=size1; ++i)
	for (register int j=1; j<=size2; ++j)
	for (register int k=d[a[i]][b[j]]; k<=mid; ++k) add(i,size1+(j-1)*mid+k,1),add(size1+(j-1)*mid+k,i,0);
	for (register int i=size1+1; i<=size1+size2*mid; ++i) add(i,t,1),add(t,i,0);

	if (dinic()>=size1) return true;
	else return false;
}

int main(){
	memset(d,60,sizeof(d)); 
	scanf("%d%d",&n,&m);
	for (register int i=1; i<=n; ++i) scanf("%s",str[i]+1); 
	for (register int i=1; i<=n; ++i)
	for (register int j=1; j<=m; ++j)
	{
		id[i][j]=++tot;
		if (str[i][j]=='.') a[++size1]=id[i][j];
		if (str[i][j]=='D') b[++size2]=id[i][j];
	}
	for (register int i=1; i<=n; ++i)
	for (register int j=1; j<=m; ++j) if (str[i][j]=='D') init(i,j);
	
	int l,r,mid,ans;
	l=0; r=n*m; ans=-1;
	while (l<=r)
	{
		mid=l+r>>1;
		if (jay(mid)) ans=mid,r=mid-1;
		else l=mid+1;
	}
	if (ans==-1) puts("impossible");
	else printf("%d\n",ans);
return 0;
}
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看REAdMe.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看REAdMe.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看READme.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值