日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Tarjan有向图强连通分量

發(fā)布時間:2025/3/21 编程问答 48 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Tarjan有向图强连通分量 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

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ù)字化實踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀

總結(jié)

以上是生活随笔為你收集整理的Tarjan有向图强连通分量的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。