BZOJ 1189: [HNOI2007]紧急疏散evacuate

远古时期的HNOI,这题感觉内存开小了吧,虽然数据也不是满的。如果是满的数据,128MB根本不够啊。
20 20
.DDDDDDDDDDDDDDDDDD.
D..........D.......D
D.......D..........D
D..................D
D.....D...D........D
D........D....D....D
D.............D....D
D....D...D.........D
D..................D
D..................D
D....D......D......D
D..................D
D...........D......D
D.....D............D
D..................D
D.D..D..D...D......D
D..................D
D....D...D..D......D
D...D..D..D...D....D
.DDDDDDDDDDDDDDDDDD.
不考虑数据和内存的问题的话,预处理距离后,二分枚举答案,通过跑最大流来验证即可。
#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=1e6+5;
int s,t;
int dep[M]; 
int cnt,hh,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);
	hh=max(hh,cnt);
	if (dinic()>=size1) return true;
	else return false;
}

int main(){
//	freopen("1189.in","r",stdin);
//	freopen("1189.out","w",stdout); 

	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);
//	printf("%d\n",hh); 
return 0;
}
BZOJ 1189: [HNOI2007]紧急疏散evacuateBZOJ 1189: [HNOI2007]紧急疏散evacuate Love_xyh 发布了86 篇原创文章 · 获赞 85 · 访问量 1765 私信 关注
上一篇:PAT.人口普查(简单哈希思想的应用)


下一篇:MATLAB作图之二