Tarjan有向图强连通分量
Tarjan有向圖強(qiáng)連通分量
本文僅供娛樂,不喜勿噴
一、強(qiáng)連通分量
有向圖強(qiáng)連通分量:在有向圖G中,如果兩個頂點(diǎn)vi,vj間(vi>vj)有一條從vi到vj的有向路徑,同時還有一條從vj到vi的有向路徑,則稱兩個頂點(diǎn)強(qiáng)連通(strongly connected)。如果有向圖G的每兩個頂點(diǎn)都強(qiáng)連通,稱G是一個強(qiáng)連通圖。有向圖的極大強(qiáng)連通子圖,稱為強(qiáng)連通分量(strongly connected components)。
通俗地說:一個強(qiáng)連通分量內(nèi)的點(diǎn)兩兩相互可達(dá)。
它可能長得像這樣
丑陋的圖
二、咋求啊?
Tarjan——一種由Robert Tarjan提出的求解有向圖強(qiáng)連通分量的線性時間的算法
我們考慮DFS的時候記錄一些東西
? 1. dfn[u] 第一次訪問u的時間戳
? 2. low[u] u及u的子孫中所能到達(dá)的最小的時間戳 (啥啊?請看下文)
我這里剛好有一些記錄這2個東西的方法:
? 1. 如果從u出發(fā)的點(diǎn)已經(jīng)沒了, 則low[u]=dfn[u]=當(dāng)前時間戳
? 2. 如果下一個將訪問的點(diǎn)v未被訪問過(即dfn[v]=0), 則v在u的子孫中l(wèi)ow[u]應(yīng)該和low[v]取最小值
? 3.如果下一個將訪問的點(diǎn)v已經(jīng)被訪問過了,那么只有返祖邊有用對吧?只需用返祖邊連接的另一節(jié)點(diǎn)的dfn來更新當(dāng)前的low
? (假設(shè)通過橫插邊與剛訪問的東西連接了,那么是不是不可能與剛訪問的組成強(qiáng)連通分量吧?,如果可以的話,剛剛就應(yīng)該做到這個u了,對吧)
咱們再看一張不優(yōu)美的圖
? 1. 圖中黑點(diǎn)即為原圖中的點(diǎn)
? 2. 黑邊是我們在dfs時經(jīng)過的邊,我們叫它樹邊
? 3. 紅邊為其他比較奇怪的邊, 標(biāo)號為1、2的邊我們叫它橫叉邊,標(biāo)號為3的邊我們叫他反祖邊
讓我們稍微想想:如果u的子孫中可以到達(dá)時間戳比u更小的, 是不是他們是強(qiáng)連通的。但是我們要求的是強(qiáng)連通分量呀。
那么再繼續(xù)想想——是不是如果u的子孫中可以到達(dá)時間戳中的最小值和dfn[u]一樣,那么是不是u就是這個強(qiáng)連通分量的根啊(好像是叫根的)。
但是如果滿足這個條件,并不是u的所有子孫和他都可以組成一個強(qiáng)連通分量。
如圖所示:
咋整呢?一個棧就可以了。每次訪問u就加到棧里,如果u為一個強(qiáng)連通分量的根,那么就把棧頂至u的元素都彈出,他們都是一個強(qiáng)連通分量的,這個得自己想想。
好像挺簡單的,看看代碼吧。
#include <stdio.h> #include <cstdlib> #include <cstring> #include <cmath> #include <vector> #include <iostream> #include <algorithm> using namespace std; const int MAXN = 10005; const int MAXM = 100005; struct Edge {int to, nxt; } edge[MAXM]; vector <int> scc[MAXN]; int fir[MAXN], ecnt, stk[MAXN], dfn[MAXN], low[MAXN]; int n, m, scnt, timer, top, col[MAXN]; void addedge(int u, int v) {edge[++ecnt].to = v;edge[ecnt].nxt = fir[u];fir[u] = ecnt; } void tarjan(int u) {dfn[u] = low[u] = ++timer;stk[++top] = u;for (int e = fir[u]; e; e = edge[e].nxt) {int v = edge[e].to;if (!dfn[v]) { //未訪問過,即v位u的子孫tarjan(v);low[u] = min(low[u], low[v]); //用他的low更新} else if (!col[v]) //沒有顏色標(biāo)記,即還未確定所在強(qiáng)連通分量,即為祖先low[u] = min(low[u], dfn[v]); //用dfn更新}if (dfn[u] == low[u]) {col[u] = ++scnt;scc[scnt].push_back(u);while (stk[top] != u) { //彈出top至u的節(jié)點(diǎn)scc[scnt].push_back(stk[top]);col[stk[top--]] = scnt;}--top;} } int main() {scanf("%d%d", &n, &m);for (int i = 1; i <= n; ++i)scanf("%d", &val[i]);for (int i = 1; i <= m; ++i) {int u, v;scanf("%d%d", &u, &v);addedge(u, v);}for (int i = 1; i <= n; ++i)if (!dfn[i])tarjan(i);for (int i = 1; i <= scnt; ++i) {for (int j = 0; j < scc[i].size(); ++j)printf("%d ", scc[i][j]);putchar('\n');}return 0; }有啥用啊?
可以把奇怪的圖變成有向無環(huán)圖(DAG),然后就可以拓?fù)渖兜牧恕?br /> 然而,Tarjan縮點(diǎn)重新建圖的寫法我始終沒找到。
來一道模板題的丑陋的代碼吧。(LUOGU 2341 受歡迎de奶牛)
#include <stdio.h> #include <cstring> #include <cstdlib> #include <cmath> #include <iostream> #include <algorithm> using namespace std; const int MAXN = 10005; const int MAXM = 50005; struct Edge {int to, nxt; } edge[MAXM]; int n, m, fir[MAXN], dfn[MAXN], low[MAXN], stk[MAXN], top; int degree[MAXN], col[MAXN], sum, cnt[MAXN], ans, ecnt, timer; void addedge(int u, int v) {edge[++ecnt].to = v;edge[ecnt].nxt = fir[u];fir[u] = ecnt; } void tarjan(int u) {dfn[u] = low[u] = ++timer;stk[++top] = u;for (int e = fir[u]; e; e = edge[e].nxt) {int v = edge[e].to;if (!dfn[v]) {tarjan(v);low[u] = min(low[u], low[v]);} else if (!col[v])low[u] = min(low[u], dfn[v]);}if (low[u] == dfn[u]) {col[u] = ++sum;while (stk[top] != u)col[stk[top--]] = sum;--top;} } int main() {scanf("%d%d", &n, &m);for (int i = 1; i <= m; ++i) {int u, v;scanf("%d%d", &u, &v);addedge(u, v);}for (int i = 1; i <= n; ++i)if (!dfn[i])tarjan(i);for (int u = 1; u <= n; ++u) {++cnt[col[u]];for (int e = fir[u]; e; e = edge[e].nxt) {int v = edge[e].to;if (col[u] != col[v])++degree[col[u]];}}int temp = 0;for (int i = 1; i <= sum; ++i)if (degree[i] == 0) {++temp;ans = cnt[i];}if (temp == 1) printf("%d\n", ans);else puts("0");return 0; }轉(zhuǎn)載于:https://www.cnblogs.com/herald/p/9830743.html
《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的Tarjan有向图强连通分量的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java并发基础01. 传统线程技术中创
- 下一篇: 磁金融宣布完成1.2亿元B轮融资,宽带资