题意:
有一个
n
n
n个点的无重边无自环的无向图,有两个人,第一个人要从
a
1
a1
a1点到
a
2
a2
a2点再回来来回
a
n
an
an次,第二个人要从
b
1
b1
b1点到
b
2
b2
b2点再回来来回
b
n
bn
bn次。图中有一些边是只能经过两次,其中来回一次算经过这条边两次。问你这两个人在这个图上是否能完成这么多次来回。多组数据。所有数据
<
=
50
<=50
<=50。
题解:
感觉
y
_
i
m
m
o
r
t
a
l
y\_immortal
y_immortal大佬抬我一手。
s
r
o
y
_
i
m
m
o
r
t
a
l
o
r
z
sro\ y\_immortal\ orz
sro y_immortal orz
怎么说的,我还是非常的年轻,上来就口胡了一个假的做法,然后一写,果然假掉了。然后我看了看题解,发现确实有一些问题。首先一个想法是,我们发现,如果去走一个只能经过经过两次的桥,回来的时候走另一个桥,一定可以有一种优劣程度不变的方法是来回都走这条边。原因是如果去的时候走了这条边,这条边的第二次要让另一个人走,那么一定存在一种方案,使得第一个人去的时候就换一条边,第二个人两次都走这条边。于是这些只能走两遍的边我们可以看作只能让一个人走。这样的话从源点向两个起点分别连 a n , b n an,bn an,bn,然后两个终点分别向汇点连 a n , b n an,bn an,bn的流量,然后跑最大流。
然后我就发现这个做法假掉了,它能过30分,但是非常假。它有两个地方有问题,第一个是,满流的原因可能是 a 1 a1 a1给 b 2 b2 b2提供了 x x x的流量, b 1 b1 b1给 a 2 a2 a2也提供了 x x x的流量,从而达成了满流。第二个问题是,我们这样并不能满足只能经过两次的边是被同一个人经过的,可能第一次第一个人经过了这条边的正向边,第二次第二个人又经过了这条边的反向边,于是就会不合法。
尽管有以上的两个问题,我们并不是不可以用网络流解决这个问题了。我们的做法是交换 b 1 b1 b1和 b 2 b2 b2,让源与 b 2 b2 b2连边,汇与 b 1 b1 b1连边,再跑一遍最大流,如果仍然能满流,就说明存在合法方案。下面解释一下为什么两遍都合法就不会有上述的两个问题了。
首先是第一个问题。我们假设第一次的时候我们从 a 1 a1 a1向 b 2 b2 b2流了 x x x的流量,从 b 1 b1 b1向 a 2 a2 a2流了 x x x的流量,那么从 a 1 a1 a1到 a 2 a2 a2流了 a n − x an-x an−x的流量。而由于第二次也满流了,图是一个无向图,于是我们还是可以从 a 1 a1 a1到 a 2 a2 a2流过去 a n − x an-x an−x的流量, b 2 b2 b2到 b 1 b1 b1流过去 b n − x bn-x bn−x的流量,那么就可以说明 a 1 a1 a1到 b 1 b1 b1能流 x x x的流量。现在我们可以从 a 1 a1 a1到 b 1 b1 b1流过去 x x x的流量,也可以从 a 1 a1 a1到 b 2 b2 b2流过去 x x x的流量,由于边是无向的,于是也可以从 b 1 b1 b1到 a 1 a1 a1流过去 x x x的流量,于是就有了一条 b 1 − > a 1 − > b 2 b1->a1->b2 b1−>a1−>b2的路径可以流过去 x x x的流量了。这样就说明存在一种方案让单独通过让 b 1 b1 b1的流量到 b 2 b2 b2也可以满流了。而 a 1 a1 a1和 a 2 a2 a2也可以同理证明,这样就解决了第一个问题了。
下面说一下第二个问题。如果第一次一条从 a 1 a1 a1到 a 2 a2 a2的路径正向经过了一条经过次数不能超过 2 2 2的边,而一条从 b 1 b1 b1到 b 2 b2 b2的路径反向经过了这条边,那么在调换 b 1 b1 b1和 b 2 b2 b2之后,就没法再从 b 2 b2 b2到 b 1 b1 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;
}