在乙個有向圖中,如果某兩點間都有互相到達的路徑,那麼稱中兩個點強連通,如果任意兩點都強連通,那麼稱這個圖為強連通圖;乙個有向圖的極大強連通子圖(不被原圖其它強連通子圖包含)稱為強連通分量。
tarjan 演算法可以在 $o(n + m)$ 的時間內求出乙個圖的所有強連通分量。
若將有向圖的強連通分量都視作乙個點,則原圖會形成有向無環圖(dag)。
棧,dfs 樹中同一棵子樹中的點在棧中是相鄰的。
$\mathrm(u)$ 表示進入節點 $u$ 時的時間(時間截)。
$\mathrm(u)$ 表示由節點 $u$ 開始搜尋所能到達的點中,在搜尋樹上是 $u$ 的祖先且 $\mathrm$ 最小的節點的 $\mathrm$。
選定乙個節點作為根節點,開始 dfs;
初始化當前點的 $\mathrm$ 和 $\mathrm$ 均為當前時間截,並進棧;
遍歷當前點 $v$ 的所有鄰接點( $v$ 出邊連的點);
如果某個鄰接點 $u$ 在棧中,更新 $\mathrm(v) = \min(\mathrm(v), \mathrm(u))$;
如果某個鄰接點 $u$ 不在棧中,則對 $u$ 進行 dfs,完成後更新 $\mathrm(v) = \min(\mathrm(v), \mathrm(u))$;
$v$ 所有鄰接點都完成 dfs 後,如果滿足 $\mathrm(v) = \mathrm(v)$,則將棧中從 $v$ 到棧頂的所有元素出棧,並標記為乙個強連通分量。
如果某個鄰接點 $u$ 在棧中,更新 $\mathrm(v) = \min(\mathrm(v), \mathrm(u))$;$u$ 已被訪問過且還未出棧,說明 $v$ 找到它的祖先 $u$,並形成了乙個環,此時要用 $u$ 去更新 $v$ 的最遠祖先。
如果某個鄰接點 $u$ 不在棧中,則對 $u$ 進行 dfs,完成後更新 $\mathrm(v) = \min(\mathrm(v), \mathrm(u))$;點 $u$ 出發能到達的最遠祖先,點 $v$ 一定也能到達。
$v$ 所有鄰接點都完成 dfs 後,如果滿足 $\mathrm(v) = \mathrm(v)$,則將棧中從 $v$ 到棧頂的所有元素出棧,並標記為乙個強連通分量。如果當前點 $v$ 為根的子樹下,無論怎麼走也無法到達 $v$ 的祖先,說明整棵子樹下的點組成乙個強連通分量。因為從 $v$ 進棧後,所有進棧的點都是子樹內的點,所以這個點在棧中的位置到棧頂位置中的點為同乙個強連通分量。
#include #include const int size = 100005;
int scc[size], scctot;
int st[size], top;
int low[size], dfn[size], time;
int h[size], to[size << 1], nxt[size << 1], tot;
int min(int x, int y)
void add(int x, int y)
void tarjan(int x) else if (!scc[y])
} if (low[x] == dfn[x])
top--;
// x 出棧 }}
int main()
for (int i = 1; i <= n; i++) if (!dfn[i]) tarjan(i);
printf("%d\n", scctot);
for (int i = 1; i <= n; i++) printf("%d ", scc[i]);
return 0;
}
強連通分量 tarjan求強連通分量
雙dfs方法就是正dfs掃一遍,然後將邊反向dfs掃一遍。挑戰程式設計 上有說明。雙dfs 1 include 2 include 3 include 4 include 5 6using namespace std 7const int maxn 1e4 5 8 vector g maxn 圖的鄰...
強連通分量 tarjan
洛谷 p2812 校園網路 洛谷 p3387 縮點 include include include include include using namespace std struct arrbot 1100000 d 1100000 int head 20000 h 20000 stack 200...
強連通分量 tarjan
struct enodeedge maxm int p maxn ec void inserte int u,int v,int w int dfn maxn ctime,low maxn 時間戳,時間戳計數,祖先時間。int gid maxn gc 分量陣列,分量計數。bool ins maxn ...