zoj 3583

 

两种做法

1:

本来想的是枚举每个点u,从s搜到u,再从t搜到u,如果两条路径只有u这一个公共点,则u可以在简单路径上,后来举出了一个反例就推翻掉了,囧。。。。。。

但现在发现这个想法是下一个想法的铺垫。

模型是:是否存在s->u的一条路径与t->u的另一条路径只有u这一个交点

单纯的搜索很难弄,比如广搜,搜到的路径都是最短路径,很容易就可以举出反例推翻掉。

举个例子:

先发一张比较挫的图片

zoj 3583

这个例子中,每个点都可以成为s->t的简单路径上的点,所以答案是0.

如果用搜索,很可能s->y->v->u       t->v->u,而忽略了s->y->x->u导致了错误

仔细想想怎么才可以判断是否存在两条s->u与t->u的路径只有u这个交点,比如s->y->x->u    与  t->v->u

其实,利用网络流可以很好的解决这个问题。

要判断是否存在两条s->u与t->u的路径只有u这个交点,只需建立一个超级源点,向s建一条边,容量为1,向t建一条边,容量为1,再建立一个超级汇点,假设当前枚举到了u这个点,则从u向超级汇点建一条边,容量为2.求最大流,如果流量为2,则u可以在某条简单路径上,每条边的容量都是1,所以除了连向汇点的边,其他的边都只能经过一次,所以如果有最大流为2,肯定就存在两条除了u外无交集的路径,满足条件。

问题解决了?没有,因为比如枚举到上图中的x时,两条路径为s->y->x  与 t->v->u->x,而枚举u的时候u、x的边是从x流向u,一条边不能同时两头流,所以可以进行拆点,

a->a+n;a+n->b;b->b+n;b+n->a,容量都为1,在这里拆点的好处就是边可以双向流而互不影响。

这样问题就完美解决了。

代码就不贴了。

 

 

2:

并查集做法:

枚举每个点去掉,判断整幅图中有那些点与s、t不在同一集合中,这些点都不在s、t的简单路径上。

因为如果去掉某个点后的图中某些点搜不到s和t,那么证明原图中s、t到达这些点的路径必然存在交点,所以这些点都不在s到t的简单路径上

贴上wa多次后ac的代码

View Code
#include<cstdio>
#include<cstring>
int g[110][110],fa[110],n;
void init(){
for(int i=0;i<=n;i++) fa[i]=i;
}
int find(int x){
return x==fa[x]?x:fa[x]=find(fa[x]);
}
void unio(int a,int b){
int x=find(a);
int y=find(b);
if(x!=y) fa[x]=y;
}
int flag[110];
int main(){
int m,s,t;
int i,j,a,b,k;
while(scanf("%d%d%d%d",&n,&m,&s,&t)!=EOF){
memset(g,0,sizeof(g));
for(i=0;i<m;i++){
scanf("%d%d",&a,&b);
g[a][b]=g[b][a]=1;
}
memset(flag,0,sizeof(flag));
int ans=0;
for(i=0;i<n;i++){
init();
for(j=0;j<n;j++)
for(k=j+1;k<n;k++)
if(i!=j&&i!=k&&g[j][k])
unio(j,k);
for(j=0;j<n;j++)
if(j!=i&&find(j)!=find(s) && find(j)!=find(t))
flag[j]=1;
}
for(i=0;i<n;i++) if(flag[i]) ans++;
printf("%d\n",ans);
}
}



你可能感兴趣的:(ZOJ)