Tarjan 強連通分量 + 解釋
阿新 • • 發佈:2018-12-11
日常補東西 這次是Tarjan演算法 原理還是別翻我這裡了......但其他的肯定很詳細~~
強連通分量
先給個題目 見下
題目大意:n (2 ≤ n ≤ 10000) 個點,m (2 ≤ m ≤ 50000) 條邊 求大於1的強連通分量的個數好了Tarjan演算法走起
關於遍歷
Tarjan 演算法最主要的還是兩個陣列
dfn 用來表示當前點的dfs序編號 (同樹剖那個id)
low 則是表示當前點所在強連通分量的 ' 根 ' 就是該強連通分量的點中編號最小的那個 主要用途就是當 dfn[p] == low[p] 時說明該強連通分量搜完了 然後存下來
還有一個數組 (我個人叫他from) 用來存某點所在的強連通分量的編號 但本題用不到 這個更新...放在彈出棧的迴圈裡
遍歷的話這裡用棧(stick然而我嫌名字太長用que代替) 每搜到一個點就丟進去
如何遍歷呢
先把最重要的 dfn 和 low 的 值賦了 一開始都等於遍歷的編號一個點的 dfn 值 等於其 low 值 的時候 說明這是一個強連通分量的根 開始的時候是一樣的 就是把每個點都當成一個強連通分量 之後再去搜索 等到搜完一圈了 此時的點就可以更新到之前某處的點 回溯之後該點的上一個點 上兩個點 上三個點......就都能連到一塊了 這樣一個強連通分量就出來了
但所有的點都要搜到哇 沒搜到的點的話還要繼續哇
於是用一個日常的 o 陣列 判斷是否找過 沒找過就從那個點開始 tarjan 即可
遍歷完了怎麼辦? 開始彈出該強連通分量的所有點 當然別忘了彈出根 建議用 do{} while() 然而我是用 while() 外面再加一次的
然後啥都沒了
之後根據題目所需自己瞎搞搞就好 本題程式碼放下來 連結這裡再放一個
#include <cstdio> using namespace std; const int MAXN = 10010; const int MAXM = 100010; struct edge { int to,next; } e[MAXM]; int first[MAXN],dfn[MAXN],low[MAXN],que[MAXN],num[MAXN]/*,id[MAXN]*/; int g,tot,t; short o[MAXN]; int min(int x,int y) {return x < y ? x : y;} void add(int x,int y) { e[++tot].next = first[x]; e[tot].to = y; first[x] = tot; } void tarjan(int p) { dfn[p] = ++tot; low[p] = tot; que[++t] = p; o[p] = 1; int b; for (int a = first[p] ; a ; a = e[a].next) { b = e[a].to; if (!dfn[b]) tarjan(b),low[p] = min(low[p],low[b]); else if (o[b]) low[p] = min(low[p],dfn[b]); } if (dfn[p] == low[p]) { ++g; while (que[t] != p) /*id[que[t]] = p,*/ ++num[g],o[que[t--]] = 0; ++num[g],o[que[t--]] = 0; } } int main() { int n,m,x,y; scanf("%d%d",&n,&m); while (m--) scanf("%d%d",&x,&y),add(x,y); tot = 0; for (int a = 1 ; a <= n ; ++ a) if (!o[a]) tarjan(a); tot = 0; for (int a = 1 ; a <= g ; ++ a) if (num[a] > 1) ++tot; printf("%d\n",tot); return 0; }