算法學習筆記(Tarjan)

本文介紹 T a r j a n Tarjan Tarjan求強聯通分量、找割點和割邊、找環。

Tarjan求強聯通分量

例題:【模板】有向圖縮點

題目描述

給定一個 n n n m m m邊的有向圖(保證不存在重邊與自環,但不保證連通),請你求出其中所有“大小大于 1 1 1”的強聯通分量的大小,如果不存在則輸出 ? 1 ?1 ?1

將所有強聯通分量大小按從小到大順序輸出。

輸入描述

第一行兩個整數 n , m n,m n,m ( 1 ≤ n , m ≤ 2 × 1 0 5 ) (1 \leq n,m \leq 2 \times 10^5) (1n,m2×105)

接下來 m m m行,每行一條邊 ( x , y ) (x,y) (x,y),表示存在一條由點 x x x到點 y y y的邊。 ( 1 ≤ x , y ≤ n ) (1≤x,y≤n) (1x,yn)

輸出描述

一行從小到大輸出所有“大小大于 1 1 1”的強聯通分量的大小,若不存在則輸出 ? 1 ?1 ?1

輸入樣例1

4 4
1 2
2 3
3 1
1 4

輸出樣例1

3

強連通:在有向圖 G G G中,如果兩個點 u u u v v v是互相可達的,即從 u u u出發可以到達 v v v,從 v v v出發可以到達 u u u,則稱 u u u v v v是強連通的。如果 G G G中任意兩個點都是互相可達的,稱 G G G是強連通圖。

強連通分量(SCC):如果一個有向圖 G G G不是強連通圖,那么可以把它分成多個子圖,其中每個子圖的內部是強連通的,而且這些子圖已經擴展到最大,不能與子圖外的任意點強連通,稱這樣的一個“極大強連通”子圖是 G G G的一個強連通分量。

接下來說明一個定理: S C C SCC SCC,從其中任意一個點出發,都至少有一條路徑能繞回到自己。

明白這點之后,解釋一下 T a r j a n Tarjan Tarjan求強連通分量的流程。

首先對于每一個節點,都有兩個量: d f n dfn dfn l o w low low d f n dfn dfn表示該節點的 d f s dfs dfs序, l o w low low則表示該節點能返回到的最遠祖先(對圖跑 d f s dfs dfs生成搜索樹,樹上節點有祖先),初始時 l o w low low就等于自己的 d f s dfs dfs序,在之后更新的時候,有兩種情況可以更新 l o w low low的值。一種是一個節點有一條有向邊指向自己在搜索樹上的祖先時,比較自己的 l o w low low和被訪問到的祖先的 d f n dfn dfn,將自己的 l o w low low更新為兩者中的最小值。還有一種情況就是 d f s dfs dfs回溯時,比較自己的 l o w low low和兒子的 l o w low low,將自己的 l o w low low更新為兩者中的較小值。更新完回來之后,回到 d f n = l o w dfn = low dfn=low的點,作為強連通分量的根。將剛剛修改過 l o w low low值的點收攏,作為一個強連通分量。

特殊的,如果一個點有一條有向邊指向了一個強連通分量,那么我們不應該去更新它的 l o w low low

為了區別這種情況,我們會使用棧來分離不同的 S C C SCC SCC,每訪問一個點就將點丟入棧中,而每找出一個 S C C SCC SCC,則將這個 S C C SCC SCC中的所有點從棧中彈出。之后有邊指向這個 S C C SCC SCC則不再理會(指向的點不在棧中說明已經屬于一個 S C C SCC SCC)。

時間復雜度為: O ( n + m ) O(n + m) O(n+m)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 9, T = 20;vector<int> g[N];int dfn[N], low[N], stk[N], top, idx;
int tot, cnt[N];
bitset<N> ins;//tarjan的本質是dfs
void tarjan(int x)
{//1.初始化dfn和lowdfn[x] = low[x] = ++ idx;//2.入棧并標記stk[++ top] = x;ins[x] = true;//3.遍歷所有兒子for(const auto &y : g[x]){//判斷是否是回點if(!dfn[y]){tarjan(y);low[x] = min(low[x], low[y]);}else if(ins[y]) low[x] = min(low[x], dfn[y]);}//4.收攏if(low[x] == dfn[x]){//新開一個顏色tot ++;while(stk[top + 1] != x){//計數cnt[tot] ++;//取消標記ins[stk[top --]] = false;}}
}void solve()
{int n, m;cin >> n >> m;for(int i = 1;i <= m; ++ i){int x, y;cin >> x >> y;g[x].push_back(y);}for(int i = 1;i <= n; ++ i)if(!dfn[i])tarjan(i);vector<int> v;for(int i = 1;i <= tot; ++ i)if(cnt[i] > 1)v.push_back(cnt[i]);if(v.size()){sort(v.begin(), v.end());for(const auto &i : v)cout << i << ' ';}else cout << -1 << '\n';
}
int main()
{ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);int _ = 1;while(_ --)solve();return 0;
}

Tarjan求割點和割邊

例題:【模板】無向圖求割點、割邊

題目描述

給一個 n n n m m m邊的無向圖(無重邊與自環),請求出圖中所有割點和割邊的數量。

輸入描述

第一行兩個整數 n , m n,m n,m ( 1 ≤ n , m ≤ 2 × 1 0 5 ) (1 \leq n,m \leq 2 \times 10^5) (1n,m2×105)

接下來 m m m行,每行兩個整數 x , y x,y x,y表示一條 x x x y y y之間的雙向邊。 ( 1 ≤ x , y ≤ n ) (1 \leq x,y \leq n) (1x,yn)

輸出描述

一行兩個整數,表示割點和割邊的數量。

輸入樣例1

4 4
1 2
3 2
2 4
1 3

輸出樣例1

1 1

解釋

割點為 2 2 2,割邊為 2 ? 4 2?4 2?4


割點和割邊:無向圖中所有能互通的點組成了一個“連通分量”。在一個連通分量中有一些關鍵的的點,如果刪除它們,會把這個連通分量斷開分為兩個或更多,這種點稱為割點。同樣的,如果在一個連通分量中刪除一條邊,把這個連通分量分成了兩個,這條邊稱為割邊。

對于一個點:分兩種情況,一是不作為搜索樹的根的節點,只要在由這個節點的兒子中,有一個節點不連通到該節點的祖先( l o w [ y ] ≥ d f n [ x ] low[y] \geq dfn[x] low[y]dfn[x]),那么這個節點是割點(由該節點作為分界線,分割了它的兒子做根的子樹和它的父親)。二是作為搜索樹的根的節點,需要兩個兒子不連通到該節點( l o w [ y ] ≥ d f n [ x ] low[y] \geq dfn[x] low[y]dfn[x]),那么這個節點是割點(刪除這個根,兩棵子樹就被分離開來了)。

而對于一條邊,只要后邊的點回不到前邊的點( l o w [ y ] ≥ d f n [ x ] low[y] \geq dfn[x] low[y]dfn[x]),那么這個點就是割邊。

那么計算割點割邊只需要看是否滿足上述條件就行。

#include <bits/stdc++.h>
#include <assert.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 9;vector<int> g[N];int dfn[N], low[N], idx, cut, es;void tarjan1(int x, int fa)
{dfn[x] = low[x] = ++ idx;int child = 0;for(const auto &y: g[x]){//1.不走父親if(y == fa)continue;//2.判斷是否是搜索樹的兒子if(!dfn[y]){tarjan1(y, x);low[x] = min(low[x], low[y]);child += (low[y] >= dfn[x]);}else low[x] = min(low[x], dfn[y]);}if((!fa && child >= 2) || (fa && child >= 1))cut ++;
}void tarjan2(int x, int fa)
{dfn[x] = low[x] = ++ idx;for(const auto &y : g[x]){if(y == fa)continue;//如果y沒被走過,就往下走if(!dfn[y]){//此時y是x在搜索樹中的兒子tarjan2(y, x);low[x] = min(low[x], low[y]);//如果y回不到自身以及父親樹上if(low[y] > dfn[x])es ++;}else low[x] = min(low[x], dfn[y]);}
}set<pair<int, int> > st;void solve()
{int n, m;cin >> n >> m;for(int i = 1;i <= m; ++ i){int x, y;cin >> x >> y;g[x].push_back(y);g[y].push_back(x);}for(int i = 1;i <= n; ++ i)if(!dfn[i])tarjan1(i, 0);for(int i = 1;i <= n; ++ i)dfn[i] = low[i] = 0;idx = 0;for(int i = 1;i <= n; ++ i)if(!dfn[i])tarjan2(i, 0);cout << cut << ' ' << es << '\n';
}
int main()
{ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);int _ = 1;while(_ --)solve();return 0;
}

Tarjan找環

例題:【模板】Tarjan找環

題目描述

給定一個 n n n n n n邊的無向連通圖(不含重邊與自環),請你求出圖中環的大小。

可以證明圖中存在且僅存在一個環。

輸入描述

第一行一個整數表示測試樣例數量 T T T ( 1 ≤ T ≤ 1000 ) (1 \leq T \leq 1000) (1T1000)

對于每組測試樣例,第一行一個整數 n n n ( 1 ≤ n ≤ 2 × 1 0 5 ) (1 \leq n \leq 2 \times 10^5) (1n2×105)

接下來 n n n行,每行一條無向邊 u , v u,v u,v ( 1 ≤ u , v ≤ n ) (1 \leq u,v \leq n) (1u,vn)

輸出描述

對于每組測試樣例,輸出一個整數表示環的大小。

輸入樣例1

2
5
1 2
1 3
2 3
3 4
4 5
4
1 2
2 3
3 4
1 4

輸出樣例1

3
4

對于找環,找出一個強連通分量就是環。因為 n n n n n n邊,一定存在且僅存在一個環(一棵樹 n ? 1 n - 1 n?1條邊,多連一條邊必定生成一個環)。于是只要在找強連通分量的代碼上稍加修改就行。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 9;vector<int> g[N];int dfn[N], low[N], idx, ans;
int stk[N], top;
bitset<N> ins;void tarjan(int x, int fa)
{dfn[x] = low[x] = ++ idx;stk[++ top] = x;ins[x] = true;for(const auto &y : g[x]){if(y == fa)continue;if(!dfn[y]){tarjan(y, x);low[x] = min(low[x], low[y]);}else if(ins[x])low[x] = min(low[x], dfn[y]);}if(low[x] == dfn[x]){int cnt = 0;while(stk[top + 1] != x){cnt ++;ins[stk[top --]] = false;}if(cnt > 1){ans = cnt;return;}}}void solve()
{int n;cin >> n;for(int i = 1;i <= n; ++ i){g[i].clear();stk[i] = dfn[i] = low[i] = 0;ins[i] = false;}stk[n + 1] = 0;ans = idx = top = 0;for(int i = 1;i <= n; ++ i){int x, y;cin >> x >> y;g[x].push_back(y);g[y].push_back(x);}tarjan(1, 0);cout << ans << '\n';
}
int main()
{ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);int _;cin >> _;while(_ --)solve();return 0;
}

T a r j a n Tarjan Tarjan找環應該只使用于 n n n n n n邊的情況?)

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/12238.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/12238.shtml
英文地址,請注明出處:http://en.pswp.cn/web/12238.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

解決webstorm沒有vue語法提示;webstorm沒有代碼提示

解決webstorm沒有vue語法提示&#xff1b;webstorm沒有代碼提示 使用webstorm 2023.x 開發vue項目。發現死活沒有vue語法提示&#xff0c;即便是npm install、清理緩存。對比其他vue項目卻有語法提示&#xff0c;最后發現依賴庫被忽略了&#xff1a; 刪除掉node_modules 的忽略…

每日一學—K鄰算法:在風險傳導中的創新應用與實踐價值

文章目錄 &#x1f4cb; 前言&#x1f3af; K鄰算法的實踐意義&#x1f3af; 創新應用與案例分析&#x1f525; 參與方式 &#x1f4cb; 前言 在當今工業領域&#xff0c;圖思維方式與圖數據技術的應用日益廣泛&#xff0c;成為圖數據探索、挖掘與應用的堅實基礎。本文旨在分享…

linux的知識點分享

每個rpm都是獨立的&#xff0c;不需要依賴包&#xff0c;可以直接安裝成功 這個說法是不準確的。在Linux系統中&#xff0c;RPM&#xff08;Red Hat Package Manager&#xff09;軟件包管理器確實可以自動解決軟件包之間的依賴關系&#xff0c;并且通常會確保在安裝一個軟件包之…

【C/C++筆試練習】DNS劫持、三次握手、TCP協議、HTTPS、四次揮手、HTTP報文、擁塞窗口、POP3協議、UDP協議、收件人列表、養兔子

文章目錄 C/C筆試練習選擇部分&#xff08;1&#xff09;DNS劫持&#xff08;2&#xff09;三次握手&#xff08;3&#xff09;TCP協議&#xff08;4&#xff09;HTTPS&#xff08;5&#xff09;四次揮手&#xff08;6&#xff09;HTTP報文&#xff08;7&#xff09;擁塞窗口&a…

Windows內存管理 - 使用宏、斷言

DDK提供了大量的宏。在使用這些宏的時候&#xff0c;要注意一種錯誤的發生&#xff0c;這就是“側效”(Side Effect)。 宏一般由多行組成&#xff0c;如下面的形式&#xff0c;其中“\”代表換行。 #define PRINT(msg) KdPrint(("\n")); \KdPrint(msg); \KdPrint…

商務分析方法與工具(八):Python的趣味快捷-年少不知numpy好,再見才覺很簡單

Tips&#xff1a;"分享是快樂的源泉&#x1f4a7;&#xff0c;在我的博客里&#xff0c;不僅有知識的海洋&#x1f30a;&#xff0c;還有滿滿的正能量加持&#x1f4aa;&#xff0c;快來和我一起分享這份快樂吧&#x1f60a;&#xff01; 喜歡我的博客的話&#xff0c;記得…

MySQL數據庫核心面試題

數據庫中的引擎 常用的引擎有InnoDB、MyIsam、Memory三種。 MyIsam&#xff1a;組織形式分為三種&#xff1a; frm文件存儲表結構、MyData文件存儲表中的數據、MyIndex文件存儲表的索引數據。是分開存儲的。 Memory&#xff1a;基于內存的&#xff0c;訪問速度快&#xff0…

C++11特性(二)

文章目錄 右值引用和移動語義左值引用和右值引用左值與左值引用右值與右值引用 右值引用有什么用完美轉發與萬能引用 右值引用和移動語義 左值引用和右值引用 所謂的引用就是給變量起別名&#xff0c;那么左值引用和右值引用的區別其實就在于左值和右值 左值與左值引用 左值…

算法_前綴和

DP34 【模板】前綴和 import java.util.Scanner;// 注意類名必須為 Main, 不要有任何 package xxx 信息 public class Main {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的區別int n in.nextInt(),q in.ne…

JavaFX布局-HBox

JavaFX布局-HBox 常用屬性alignmentspacingchildrenmarginpaddinghgrow 實現方式Java實現Xml實現 綜合案例 HBox按照水平方向排列其子節點改變窗口大小,不會該部整體布局窗口太小會遮住內部元素&#xff0c;不會產生滾動條 常用屬性 alignment 對齊方式 new HBox().setAlign…

Angular前端項目在Apache httpd服務器上的部署

Apache Httpd和Tomcat主要區別&#xff1a;Tomcat是一個Java Servlet容器&#xff0c;用于運行Java Servlet和JavaServer Pages&#xff08;JSP&#xff09;&#xff0c;而Apache HTTP服務器是一個通用的Web服務器&#xff0c;用于提供靜態和動態內容。 Apache httpd安裝&#…

RT Thread + CLion環境搭建

RT Thread CLion環境搭建 0.前言一、準備工具1. Env RT Thread v5.12.CLion安裝3.編譯及下載工具 二、新建Env工程三、CLion配置四、運行測試 0.前言 事情的起因是最近在使用RT Thread Studio時&#xff0c;發現默認的 rtt 內核版本及交叉編譯鏈版本都過于陳舊&#xff0c;于…

SpringBoot 表單提交參數綁定 List 下標越界,超過 256,報數組越界異常

文章目錄 》原因》解決方案 》原因 Spring Validation 的 org.springframework.validation.DataBinder 類中默認限制&#xff0c;表單提交 List 元素數量超過 256 時就會拋出異常 public class DataBinder implements PropertyEditorRegistry, TypeConverter {/** Default li…

JS算法-十大排序算法(上)

思想小劇場 如果我的相對論被證明是正確的&#xff0c;德國人就會說我是德國人&#xff0c;法國人會說我是一個世界公民&#xff1b;如果我的相對論被否定了&#xff0c;法國佬就會罵我是德國鬼子&#xff0c;而德國人就會把我歸為猶太人。—愛因斯坦 以下案例都是升序 const a…

《無畏契約》游戲畫面出現“撕裂感“,你清楚背后的原理嗎?

&#x1f338;個人主頁:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 &#x1f3f5;?熱門專欄:&#x1f355; Collection與數據結構 (91平均質量分)https://blog.csdn.net/2301_80050796/category_12621348.html?spm1001.2014.3001.5482 &#x1f9c0;Java …

信息化總體架構方法_2.信息化工程建設方法

1.信息化架構模式 信息化架構一般有兩種模式&#xff0c;一種是數據導向架構&#xff0c;一種是流程導向架構。對于數據導向架構重點是在數據中心&#xff0c;BI商業智能等建設中使用較多&#xff0c;關注數據模型和數據質量&#xff1b;對于流程導向架構&#xff0c;SOA本身就…

黑馬程序員鴻蒙HarmonyOS端云一體化開發【13-15】

前置知識&#xff1a;arkts 一套開發工具&#xff0c;一套語言&#xff0c;搞定客戶端和云端兩個的編寫。其中application就是客戶端&#xff0c;cloudProgram就是云端。 開發人員->全棧開發工程師&#xff0c;降低了開發成本&#xff0c;且提供了很多現成的云服務&#xf…

AI原生實踐:測試用例創作探索

測試用例作為質量保障的核心&#xff0c;影響著研發-測試-發布-上線的全過程&#xff0c;如單元測試用例、手工測試用例、接口自動化用例、UI 自動化用例等&#xff0c;但用例撰寫的高成本尤其是自動化用例&#xff0c;導致了用例的可持續積累、更新和迭代受到非常大制約。長久…

Python并發編程 05 鎖、同步條件、信號量、線程隊列、生產者消費者模型

文章目錄 一、基礎概念二、同步鎖三、線程死鎖和遞歸鎖四、同步條件&#xff08;event&#xff09;五、信號量六、線程隊列&#xff08;queue&#xff09;1、常用方法2、queue模塊的三種模式&#xff08;1&#xff09;FIFO隊列&#xff08;2&#xff09;LIFO隊列&#xff08;3&…

【JS面試題】原型原型鏈

一、面試真題展示&#xff1a; 1. 如何準確判斷一個變量是不是數組&#xff1f; ① 使用instanceof進行判斷&#xff1a;a instanceof Array ② 使用Array.isArray()進行判斷&#xff1a;Array.isArray(a) 2. 手寫一個簡易的jQuery&#xff0c;考慮插件和擴展性&#xff1f; …