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 }

原文地址:https://www.cnblogs.com/OI-zzyy/p/11183705.html

时间: 2024-11-09 22:04:33

Redundant Paths 分离的路径(边双连通分量)的相关文章

BZOJ 1718: [Usaco2006 Jan] Redundant Paths 分离的路径( tarjan )

tarjan求边双连通分量, 然后就是一棵树了, 可以各种乱搞... ------------------------------------------------------------------------------- #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn = 5009; struct edge { int to; b

1718: [Usaco2006 Jan] Redundant Paths 分离的路径

1718: [Usaco2006 Jan] Redundant Paths 分离的路径 Time Limit: 5 Sec  Memory Limit: 64 MB链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1718 Description In order to get from one of the F (1 <= F <= 5,000) grazing fields (which are numbered 1..F) to an

BZOJ 1718: [Usaco2006 Jan] Redundant Paths 分离的路径

Description 给出一个无向图,求将他构造成双连通图所需加的最少边数. Sol Tarjan求割边+缩点. 求出割边,然后缩点. 将双连通分量缩成一个点,然后重建图,建出来的就是一棵树,因为每一条边都是桥. 然后每次合并这棵树上的叶节点两点距离LCA最远的点,这样就会形成一个环,是双连通的,然后进行(ans+1)/2次操作就可以了. 其实就是(叶节点个数+1)/2 Code #include<cstdio> #include<vector> #include<iost

Redundant Paths POJ - 3177(边—双连通分量)

题意: 在图中加边 看最少能通过加多少条边把 图变成边-双连通分量 解析: 先做一次dfs,不同的连通分量的low是不同的  注意重边 缩点 统计度为1的点  那么需要加的边为(ret+1)/2 #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <vector> #include

BZOJ1718: [Usaco2006 Jan] Redundant Paths 分离的路径

n<=5000个点m<=10000条边的无向图,求最少加几条边使它变成边双联通图,就是任意两点间都有至少2条边不相交的路径. tarjan缩点,答案是叶子节点数/2向上取整. 不过要注意这里的"叶子节点数"是指度数为1的点,并不是最后那棵树以某个点为根的叶子节点树.如果找叶子点数一定要以某个点为根,就会像我一样WA两次然后发现缩点后只有一个点时答案为0. 1 #include<stdio.h> 2 #include<string.h> 3 #incl

Redundant Paths POJ - 3177(边双连通)

Redundant Paths POJ - 3177 题意:一个无向图(有重边!!),问至少还要加多少边使得去掉任意一条边后任意两点仍可互达. 和上题poj3352基本相同,不过dfs的时候,不能用v!=f来判断是否能走,而要用当前走的边和上一条边是不是反向边 1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #include <cstring> 5 using namesp

【BZOJ1718】&amp;&amp;【POJ3177】Redundant Paths

1718: [Usaco2006 Jan] Redundant Paths 分离的路径 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 452  Solved: 239[Submit][Status][Discuss] Description In order to get from one of the F (1 <= F <= 5,000) grazing fields (which are numbered 1..F) to another f

POJ3177 Redundant Paths 双连通分量

Redundant Paths Description In order to get from one of the F (1 <= F <= 5,000) grazing fields (which are numbered 1..F) to another field, Bessie and the rest of the herd are forced to cross near the Tree of Rotten Apples. The cows are now tired of

Poj 3352 Road Construction &amp; Poj 3177 Redundant Paths(边双连通分量+缩点)

Road Construction Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 9465   Accepted: 4699 Description It's almost summer time, and that means that it's almost summer construction time! This year, the good people who are in charge of the ro