鏈接: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 }