poj 3177 Redundant Paths(边双连通分量+缩点)

链接:http://poj.org/problem?id=3177

题意:有n个牧场,Bessie 要从一个牧场到另一个牧场,要求至少要有2条独立的路可以走。现已有m条路,求至少要新建多少条路,使得任何两个牧场之间至少有两条独立的路。两条独立的路是指:没有公共边的路,但可以经过同一个中间顶点。

分析:在同一个边双连通分量中,任意两点都有至少两条独立路可达,所以同一个边双连通分量里的所有点可以看做同一个点。

缩点后,新图是一棵树,树的边就是原无向图的桥。

现在问题转化为:在树中至少添加多少条边能使图变为双连通图。

结论:添加边数=(树中度为1的节点数+1)/2

具体方法为,首先把两个最近公共祖先最远的两个叶节点之间连接一条边,这样可以把这两个点到祖先的路径上所有点收缩到一起,因为一个形成的环一定是双连通的。然后再找两个最近公共祖先最远的两个叶节点,这样一对一对找完,恰好是(leaf+1)/2次,把所有点收缩到了一起。

其实求边双连通分量和求强连通分量差不多,每次访问点的时候将其入栈,当low[u]==dfn[u]时就说明找到了一个连通的块,则栈内的所有点都属于同一个边双连通分量,因为无向图要见反向边,所以在求边双连通分量的时候,遇到反向边跳过就行了。

网上有一种错误的做法是:因为每一个双连通分量内的点low[]值都是相同的,则dfs()时,对于一条边(u,v),只需low[u]=min(low[u],low[v]),这样就不用缩点,最后求度数的时候,再对于每条边(u,v)判断low[u]是否等于low[v],若low[u]!=low[v],则不是同一个边双连通分量,度数+1即可.....

咋看之下是正确的,但是这种做法只是考虑了每一个强连通分量重只有一个环的情况,如果有多个环,则会出错。

比如这组数据:

16 21
1 8
1 7
1 6
1 2
1 9
9 16
9 15
9 14
9 10
10 11
11 13
11 12
12 13
11 14
15 16
2 3
3 5
3 4
4 5
3 6
7 8

答案是1,上面错误的做法是0

大家自己画图慢慢研究吧。。。下面贴代码

AC代码:

 1 #include<cstdio>

 2 #include<cstring>

 3 const int N=5000+5;

 4 const int M=10000+5;

 5 

 6 struct EDGE

 7 {

 8     int v,next;

 9 }edge[M*2];

10 int first[N],low[N],dfn[N],belong[N],degree[N],sta[M],instack[M];

11 int g,cnt,top,scc;

12 int min(int a,int b)

13 {

14     return a<b?a:b;

15 }

16 void AddEdge(int u,int v)

17 {

18     edge[g].v=v;

19     edge[g].next=first[u];

20     first[u]=g++;

21 }

22 void Tarjan(int u,int fa)

23 {

24     int i,v;

25     low[u]=dfn[u]=++cnt;

26     sta[++top]=u;

27     instack[u]=1;

28     for(i=first[u];i!=-1;i=edge[i].next)

29     {

30         v=edge[i].v;

31         if(i==(fa^1))

32             continue;

33         if(!dfn[v])

34         {

35             Tarjan(v,i);

36             low[u]=min(low[u],low[v]);

37         }

38         else if(instack[v])

39             low[u]=min(low[u],dfn[v]);

40     }

41     if(dfn[u]==low[u])

42     {

43         scc++;

44         while(1)

45         {

46             v=sta[top--];

47             instack[v]=0;

48             belong[v]=scc;

49             if(v==u)

50                 break;

51         }

52     }

53 }

54 int main()

55 {

56     int n,m,u,v,i,j;

57     scanf("%d%d",&n,&m);

58         g=cnt=top=scc=0;

59         memset(first,-1,sizeof(first));

60         memset(low,0,sizeof(low));

61         memset(dfn,0,sizeof(dfn));

62         memset(instack,0,sizeof(instack));

63         memset(degree,0,sizeof(degree));

64         for(i=0;i<m;i++)

65         {

66             scanf("%d%d",&u,&v);

67             {

68                 AddEdge(u,v);

69                 AddEdge(v,u);

70             }

71         }

72         for(i=1;i<=n;i++)

73             if(!dfn[i])

74                 Tarjan(1,-1);

75         for(i=1;i<=n;i++)

76         {

77             for(j=first[i];j!=-1;j=edge[j].next)

78             {

79                 v=edge[j].v;

80                 if(belong[i]!=belong[v])

81                     degree[belong[i]]++;

82             }

83         }

84         int sum=0;

85         for(i=1;i<=n;i++)

86             if(degree[i]==1)

87                 sum++;

88         int ans=(sum+1)/2;

89         printf("%d\n",ans);

90     return 0;

91 }
View Code

 

你可能感兴趣的:(Path)