洛谷3163 BZOJ3504 CQOI2014 危桥 最大流

版权声明:本文为博主原创文章,可以转载但是必须声明版权。 https://blog.csdn.net/forever_shi/article/details/88569465

题目链接

题意:
有一个 n n 个点的无重边无自环的无向图,有两个人,第一个人要从 a 1 a1 点到 a 2 a2 点再回来来回 a n an 次,第二个人要从 b 1 b1 点到 b 2 b2 点再回来来回 b n bn 次。图中有一些边是只能经过两次,其中来回一次算经过这条边两次。问你这两个人在这个图上是否能完成这么多次来回。多组数据。所有数据 < = 50 <=50

题解:
感觉 y _ i m m o r t a l y\_immortal 大佬抬我一手。 s r o   y _ i m m o r t a l   o r z sro\ y\_immortal\ orz

怎么说的,我还是非常的年轻,上来就口胡了一个假的做法,然后一写,果然假掉了。然后我看了看题解,发现确实有一些问题。首先一个想法是,我们发现,如果去走一个只能经过经过两次的桥,回来的时候走另一个桥,一定可以有一种优劣程度不变的方法是来回都走这条边。原因是如果去的时候走了这条边,这条边的第二次要让另一个人走,那么一定存在一种方案,使得第一个人去的时候就换一条边,第二个人两次都走这条边。于是这些只能走两遍的边我们可以看作只能让一个人走。这样的话从源点向两个起点分别连 a n , b n an,bn ,然后两个终点分别向汇点连 a n , b n an,bn 的流量,然后跑最大流。

然后我就发现这个做法假掉了,它能过30分,但是非常假。它有两个地方有问题,第一个是,满流的原因可能是 a 1 a1 b 2 b2 提供了 x x 的流量, b 1 b1 a 2 a2 也提供了 x x 的流量,从而达成了满流。第二个问题是,我们这样并不能满足只能经过两次的边是被同一个人经过的,可能第一次第一个人经过了这条边的正向边,第二次第二个人又经过了这条边的反向边,于是就会不合法。

尽管有以上的两个问题,我们并不是不可以用网络流解决这个问题了。我们的做法是交换 b 1 b1 b 2 b2 ,让源与 b 2 b2 连边,汇与 b 1 b1 连边,再跑一遍最大流,如果仍然能满流,就说明存在合法方案。下面解释一下为什么两遍都合法就不会有上述的两个问题了。

首先是第一个问题。我们假设第一次的时候我们从 a 1 a1 b 2 b2 流了 x x 的流量,从 b 1 b1 a 2 a2 流了 x x 的流量,那么从 a 1 a1 a 2 a2 流了 a n x an-x 的流量。而由于第二次也满流了,图是一个无向图,于是我们还是可以从 a 1 a1 a 2 a2 流过去 a n x an-x 的流量, b 2 b2 b 1 b1 流过去 b n x bn-x 的流量,那么就可以说明 a 1 a1 b 1 b1 能流 x x 的流量。现在我们可以从 a 1 a1 b 1 b1 流过去 x x 的流量,也可以从 a 1 a1 b 2 b2 流过去 x x 的流量,由于边是无向的,于是也可以从 b 1 b1 a 1 a1 流过去 x x 的流量,于是就有了一条 b 1 > a 1 > b 2 b1->a1->b2 的路径可以流过去 x x 的流量了。这样就说明存在一种方案让单独通过让 b 1 b1 的流量到 b 2 b2 也可以满流了。而 a 1 a1 a 2 a2 也可以同理证明,这样就解决了第一个问题了。

下面说一下第二个问题。如果第一次一条从 a 1 a1 a 2 a2 的路径正向经过了一条经过次数不能超过 2 2 的边,而一条从 b 1 b1 b 2 b2 的路径反向经过了这条边,那么在调换 b 1 b1 b 2 b2 之后,就没法再从 b 2 b2 b 1 b1 的方向经过这条边了,这样就得到了限制。于是第二个问题也得到了解决。

最后做法就是求两遍最大流,都满流就说明存在合法解。

感觉有点像一个构造题,反正我感觉怎么解决最初想法的两个问题是这个题的关键,确实比较有思维难度。

代码:

#include <bits/stdc++.h>
using namespace std;

int n,a1,a2,an,b1,b2,bn,ju[52][52];
int s1=51,s2=52,t1=53,t2=54,st=55,ed=56,hed[1000],cnt,res;
int dep[1000],q[1000],h,t;
char ss[60][60];
struct node
{
	int to,next,c;
}a[2000010];
inline void add(int from,int to,int c)
{
	a[++cnt].to=to;
	a[cnt].c=c;
	a[cnt].next=hed[from];
	hed[from]=cnt;
	a[++cnt].to=from;
	a[cnt].c=0;
	a[cnt].next=hed[to];
	hed[to]=cnt;
}
inline int bfs()
{
	memset(dep,0,sizeof(dep));
	dep[st]=1;
	h=1;
	t=1;
	q[h]=st;
	while(h<=t)
	{
		int x=q[h];
		for(int i=hed[x];i;i=a[i].next)
		{
			int y=a[i].to;
			if(dep[y]==0&&a[i].c)
			{
				dep[y]=dep[x]+1;
				q[++t]=y;
			}
		}
		++h;
	}
	if(dep[ed]>0)
	return 1;
	else
	return 0;
}
inline int flow(int x,int f)
{
	if(x==ed)
	return f;
	int s=0,t;
	for(int i=hed[x];i;i=a[i].next)
	{
		int y=a[i].to;
		if(dep[y]==dep[x]+1&&a[i].c&&f>s)
		{
			s+=(t=flow(y,min(a[i].c,f-s)));
			a[i].c-=t;
			a[i^1].c+=t;
		}
	}
	if(s==0)
	dep[x]=0;
	return s;
}
int main()
{
	while(~scanf("%d%d%d%d%d%d%d",&n,&a1,&a2,&an,&b1,&b2,&bn))
	{
		++a1;
		++a2;
		++b1;
		++b2;
		memset(hed,0,sizeof(hed));
		for(int i=1;i<=cnt;++i)
		{
			a[i].to=0;
			a[i].next=0;
			a[i].c=0;
		}
		cnt=1;
		res=0;
		for(int i=1;i<=n;++i)
		{
			scanf("%s",ss[i]+1);
			for(int j=1;j<=n;++j)
			{
				if(ss[i][j]=='N')
				add(i,j,2e9);
				if(ss[i][j]=='O')
				add(i,j,1);
			}
		}
		add(st,s1,an);
		add(st,s2,bn);
		add(s1,a1,an);
		add(s2,b1,bn);
		add(t1,ed,an);
		add(t2,ed,bn);
		add(a2,t1,an);
		add(b2,t2,bn);
		while(bfs())
		res+=flow(st,2e9);
		if(res!=an+bn)
		{
			printf("No\n");
			continue;
		}
		swap(b1,b2);
		memset(hed,0,sizeof(hed));
		for(int i=1;i<=cnt;++i)
		{
			a[i].to=0;
			a[i].next=0;
			a[i].c=0;
		}
		cnt=1;
		res=0;
		for(int i=1;i<=n;++i)
		{
			for(int j=1;j<=n;++j)
			{
				if(ss[i][j]=='N')
				add(i,j,2e9);
				if(ss[i][j]=='O')
				add(i,j,1);
			}
		}
		add(st,s1,an);
		add(st,s2,bn);
		add(s1,a1,an);
		add(s2,b1,bn);
		add(t1,ed,an);
		add(t2,ed,bn);
		add(a2,t1,an);
		add(b2,t2,bn);
		while(bfs())
		res+=flow(st,2e9);
		if(res==an+bn)
		printf("Yes\n");
		else
		printf("No\n");
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/forever_shi/article/details/88569465
今日推荐