Tarjan演算法求強連通圖
阿新 • • 發佈:2020-10-27
先把參考資料的傳送門放一下:
1、優質的B站視訊講解
(裡面老師給出了一個很不錯的程式碼模板)
2、tarjan求強連通分量+縮點+割點/割橋(點雙/邊雙)以及一些證明(原理簡述)
先不論縮點是怎麼做的,
根據第一個B站視訊裡的模板,
先用線性的鄰接表維護有向圖,
基本實現一下Tarjan演算法。
const int maxn = 1e5 + 10; //線性鄰接表維護有向圖 class Node{ public: int nd,nxt; Node(){}; Node(int nd_,int nxt_):nd(nd_),nxt(nxt_){}; }; Node G[maxn]; int head[maxn]; int cnt; void add(int u,int v){ G[cnt] = Node(v,head[u]); head[u] = cnt++; } void init_G(){ memset(head,-1,sizeof(head)); cnt = 0; } //下面是Tarjan演算法求強連通圖 int Low[maxn]; int Dfn[maxn]; int num = 0;//編號變數-賦值不能是0 int out[maxn];//標記被棧彈出過的元素 int Stack[maxn];//模擬棧 int cur = 0;//cur應當始終指向Stack的尾端元素 int ans = 0; /*我們假設結點的編號從1開始*/ int dfs(int u){ Low[u] = Dfn[u] = ++num;//結點編號 Stack[++cur] = u;//結點入棧 //下面開始遍歷u的臨界結點 for(int i=head[u];~i;i=G[i].nxt){ int v = G[i].nd; if(Dfn[v] == 0){//v沒有被訪問 dfs(v); Low[u] = min(Low[u],Low[v]); }else if(!out[v]){//v已經被訪問且還在棧中 Low[u] = Low[v];//形成環 } } //輸出強連通分量 if(Low[u] == Dfn[u]){ int cur_num = 0; do{ cur_num++; out[Stack[cur]]=1; }while(Stack[cur--] != u); //這裡做的處理是為了統計點個數大於1的連通區塊的總個數ans if(cur_num>1)ans++; } return 0; } int main(){ frein("in.txt"); int n,m; cin>>n>>m; init_G(); for(int i=0;i<m;i++){ int a,b; cin>>a>>b; add(a,b); } //由於圖並不一定是弱連通圖 //所以這裡對每一個沒有訪問過的結點都進行搜尋 //當然,一般這並不會讓複雜度達到O(n*n) for(int i=1;i<=n;i++){ if(!Dfn[i]){ dfs(i); } } cout<<ans<<endl; return 0; }
OK