標籤:
題意:給一個無向圖,問需要補多少條邊才可以讓整個圖變成【邊雙連通圖】,即任意兩個點對之間的一條路徑全垮掉,這兩個點對仍可以通過其他路徑而互連。
思路:POJ 3352的升級版,聽說這個圖會給重邊。先看3352的題解http://www.cnblogs.com/xcw0754/p/4619594.html。
其實與3352不同的就是重邊出現了怎麼辦?假如出現的重邊剛好是橋呢?
首先要知道,【割點】可以將兩個【點雙連通分量】隔開來,因為僅一個【點雙連通分量】中肯定無割點,那麼每兩個點對都同時處於若干個簡單環中才能當一個點撤掉仍然可以互連。
而【橋】可以將兩個【邊雙連通分量】隔開來,因為僅僅一個【邊雙連通分量】中肯定無橋,那麼每兩個點對之間肯定有2條以上的路徑可達(可以經過同1個點),當任意1條邊撤掉後每兩個點對仍然可達。
以上兩點很重要,根據第二點,我們知道,如果在一個【邊雙連通分量】中任意兩個有邊相連的點再添加任意條邊都是不影響的,任意兩點間依然有2種不同的【邊】路徑可達。但是如果橋出現了呢?那麼該橋上的兩點會同時屬於一個【邊雙連通分量】,此時就會將兩個【邊雙連通分量】合并為一。
如何判別橋是否出現重邊?我們一般以t!=far來判別是否遇到一條邊通往父親,但是現在要變了,我們要判斷是否遇到這條邊多次,如果多次,要更新本節點x的low值了,應該用low[x]=min(low[x],dfn[far])來更新,更新完之後他們的low值自然會處於同一個領導之下,此時再按照3352中用的類似於並查集的方法就可以將其徹底歸到同一個【邊雙連通分量】中。
1 #include <iostream> 2 #include <cmath> 3 #include <cstdio> 4 #include <vector> 5 #include <cstring> 6 #include <set> 7 //#include <bits/stdc++.h> 8 using namespace std; 9 const int N=5000+5;10 vector<int> vect[N];11 int low[N], dfn[N], pre[N], cnter;12 int du[N];13 vector<pair<int,int> > cutt;14 int find(int x) //尋找x的low值15 {16 if(low[x]==dfn[x]) return low[x];17 return low[x]=find( pre[low[x] ] );18 }19 20 void DFS(int x, int far)21 {22 low[x]= dfn[x]= ++cnter;23 pre[cnter]=x; //第cnter個訪問的是x24 int times=0; //記錄橋是否有兩條,若有,則要更新low值25 for(int i=0; i<vect[x].size(); i++)26 {27 int t=vect[x][i];28 if(!dfn[t])29 {30 DFS(t,x);31 low[x]=min(low[x],low[t]);32 if(low[t]>dfn[x]) cutt.push_back(make_pair(x,t));33 }34 else if(t!=far) low[x]=min(low[x],dfn[t]);35 else36 {37 if(times) low[x]=min(low[x],dfn[far]); //far-x的邊不止1條,low應該更新為far才對。38 times++;39 }40 }41 }42 43 44 int cal_bcc(int f) //找割點及雙連通分量45 {46 cutt.clear();47 memset(du,0,sizeof(du));48 memset(low,0,sizeof(low));49 memset(dfn,0,sizeof(dfn));50 memset(pre,0,sizeof(pre));51 cnter=0;52 DFS(1,0);53 54 for(int i=0; i<cutt.size(); i++)55 {56 int a=cutt[i].first;57 int b=cutt[i].second;58 du[find(a)]++;59 du[find(b)]++;60 }61 62 int ans=0;63 for(int i=1; i<=f; i++) if(du[i]==1) ans++;64 65 return ((ans+1)/2); //如果葉子數為偶數,可以互纏,葉子順序1234可以1-3,2-4。奇數就要扣掉1個,然後互纏,再加1。66 }67 68 int main()69 {70 freopen("input.txt", "r", stdin);71 int f, r, a, b, j=0;72 char s[N];73 while(cin>>f>>r)74 {75 76 for(int i=1; i<=f; i++) vect[i].clear();77 while(r--)78 {79 scanf("%d%d", &a, &b);80 vect[a].push_back(b);81 vect[b].push_back(a);82 }83 printf("%d\n",cal_bcc(f));84 }85 return 0;86 }AC代碼
POJ 3177 Redundant Paths (橋,邊雙連通分量,有重邊)