Redundant Paths 分离的路径(边双连通分量)
Posted oi-zzyy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Redundant Paths 分离的路径(边双连通分量)相关的知识,希望对你有一定的参考价值。
题干:
为了从F(1≤F≤5000)个草场中的一个走到另一个,贝茜和她的同伴们有时不得不路过一些她们讨厌的可怕的树.奶牛们已经厌倦了被迫走某一条路,所以她们想建一些新路,使每一对草场之间都会至少有两条相互分离的路径,这样她们就有多一些选择.每对草场之间已经有至少一条路径.给出所有 R ( F - 1 ≤ R ≤ 10000 )条双向路的描述,每条路连接了两个不同的草场,请计算最少的新建道路的数量,
路径由若干道路首尾相连而成.两条路径相互分离,是指两条路径没有一条重合的道路.但是,两条分离的路径上可以有一些相同的草场.
对于同一对草场之间,可能已经有两条不同的道路,你也可以在它们之间再建一条道路,作为另一条不同的道路,请输出最少的需要新建的道路数.
题解:
加减道路?加减边?比较简单地就可以想到边双连通分量。
本题要求使每一个节点都可由至少两条道路到达,那么满足条件的节点就是度数 >= 2 的节点(无向图的度)。度数为 1 的节点就是不满足题意的。
在两点间建边有三种情况:
1、两个度数 >= 2 的节点相连:没用,舍掉。
2、一个度数 >= 2 ,另一个 = 1 的两个节点相连:使一个节点满足题意。
3、两个度数 = 1 的节点相连:使两个节点满足题意。
十分显然,两个度数 = 1 的节点相连一定是最优的。这也表明,本题核心在于找到 度数 = 1 的节点。
100% 错解(点双连通分量):
像以往的点双连通分量,先缩点,求一下度数即可。
但这样的 AC 代码并不符合点双连通分量的求法(在实现过程中,需要在 tarjan 中判断掉父节点;在建边中,需要判掉重边)。
只能说在这道题中,数据并没有考虑这种打法,虽然 AC,一定是错解。
Code:
1 #include<cstdio>
2 #include<cstring>
3 #include<algorithm>
4 #define $ 5111
5 using namespace std;
6 int m,n,k,t,dfn[$],low[$],first[$],tot1,tar,sta[$],up,circle,tr=-1;
7 int sum,cir[$],out[$];
8 bool judge[$],vis[$][$];
9 struct tree int to,next; a[$<<5],aa[$<<5];
10 inline int min(int x,int y) return x<y?x:y;
11 inline void add(int x,int y)
12 a[++tot1]=(tree) y,first[x] ;
13 first[x]=tot1;
14 a[++tot1]=(tree) x,first[y] ;
15 first[y]=tot1;
16
17 inline void tarjan(int x,int dad,int tmp=0)
18 dfn[x]=low[x]=++tar;
19 sta[++up]=x;
20 for(register int i=first[x];i;i=a[i].next)
21 int to=a[i].to;
22 if(to==dad) continue;
23 if(!dfn[to])
24 tarjan(to,x);
25 low[x]=min(low[x],low[to]);
26
27 else low[x]=min(low[x],dfn[to]);
28
29 if(dfn[x]==low[x])
30 ++circle;
31 do
32 tmp=sta[up--];
33 cir[tmp]=circle;
34 while(tmp!=x);
35
36
37 signed main()
38 scanf("%d%d",&n,&m);
39 for(register int i=1,x,y;i<=m;++i)
40 scanf("%d%d",&x,&y);
41 if(vis[x][y]==0) add(x,y),vis[x][y]=vis[y][x]=1;
42
43 tarjan(1,0);
44 for(register int i=1;i<=n;++i)
45 for(register int j=first[i];j;j=a[j].next)
46 int to=a[j].to;
47 if(cir[i]!=cir[to]) out[cir[i]]++;
48
49 for(register int i=1;i<=circle;++i) if(out[i]<2) sum++;
50 circle==1?puts("0"):printf("%d\n",(sum+1)/2);
51
100% 正解(边双连通分量):
正解当然是边双连通分量。
1 inline void tarjan(int x,int opt) 2 dfn[x]=low[x]=++tar; 3 for(register int i=first[x];i;i=a[i].next) 4 int to=a[i].to; 5 if(!dfn[to]) 6 tarjan(to,i); 7 low[x]=min(low[x],low[to]); 8 if(low[to]>dfn[x]) br[i]=br[i^1]=1; 9 10 else if(i!=(opt^1)) low[x]=min(low[x],dfn[to]); 11 12 13 inline void dfs(int x) 14 in[x]=dcc; 15 for(register int i=first[x];i;i=a[i].next) 16 int to=a[i].to; 17 if(in[to]||br[i]) continue; 18 dfs(to); 19 20
同样是先缩点,求一下度数即可。
Code:
1 #include<cstdio>
2 #include<cstring>
3 #include<algorithm>
4 #define $ 5111
5 using namespace std;
6 int m,n,sum,dfn[$],low[$],first[$],tot,tar,br[$],up,circle,dcc,in[$],out[$];
7 struct tree int to,next; a[$<<5],aa[$<<5];
8 inline int min(int x,int y) return x<y?x:y;
9 inline void add(int x,int y)
10 a[++tot]=(tree) y,first[x] ;
11 first[x]=tot;
12 a[++tot]=(tree) x,first[y] ;
13 first[y]=tot;
14
15 inline void tarjan(int x,int opt)
16 dfn[x]=low[x]=++tar;
17 for(register int i=first[x];i;i=a[i].next)
18 int to=a[i].to;
19 if(!dfn[to])
20 tarjan(to,i);
21 low[x]=min(low[x],low[to]);
22 if(low[to]>dfn[x]) br[i]=br[i^1]=1;
23
24 else if(i!=(opt^1)) low[x]=min(low[x],dfn[to]);
25
26
27 inline void dfs(int x)
28 in[x]=dcc;
29 for(register int i=first[x];i;i=a[i].next)
30 int to=a[i].to;
31 if(in[to]||br[i]) continue;
32 dfs(to);
33
34
35 signed main()
36 scanf("%d%d",&n,&m); tot++;
37 for(register int i=1,x,y;i<=m;++i) scanf("%d%d",&x,&y),add(x,y);
38 tarjan(1,0);
39 for(register int i=1;i<=n;++i) if(!in[i]) dcc++,dfs(i);
40 for(register int i=2,x,y;i<=tot;++i)
41 x=a[i^1].to,y=a[i].to;
42 if(in[x]!=in[y]) out[in[y]]++;
43
44 for(register int i=1;i<=dcc;++i) if(out[i]==1) sum++;
45 dcc==1?puts("0"):printf("%d\n",(sum+1)/2);
46
以上是关于Redundant Paths 分离的路径(边双连通分量)的主要内容,如果未能解决你的问题,请参考以下文章
P2860[USACO06JAN]Redundant Paths G(边双连通分量&无向图缩点)
POJ 3177 Redundant Paths(边双连通分量)
POJ 3177 - Redundant Paths - 双连通分量