1. 程式人生 > >【學習】【Tarjan】【強連通分量】

【學習】【Tarjan】【強連通分量】

       正題之前嘮嘮幾句,寫這篇部落格的原因是因為做到了一道圖論題,發現顯然需要縮點解決,但是發現自己圖論中的縮點基本忘完了,同時與之相關的Tarjan求SCC(強連通分量Strong Connected Component)也不記得多少,才想著把圖論當中的Tarjan求SCC複習一遍。

       附上Tarjan求割邊割點的學習部落格:https://blog.csdn.net/yanyidu/article/details/79793736

正題:

       首先,先交代一大串概念(一定要熟記!)。

       1.圖的表示:我們用V表示圖G的頂點的集合,E表示圖G的邊的集合,那麼我們圖G=(V,E)。

       2.連通圖:在無向圖中,如果任意兩點間可相互到達則稱該圖為連通圖。

       3.子圖:如果存在圖G=(V,E),G1=(V1,E1),並且V1,E1分別是V,E的子集,那麼我們稱G1是G的子圖。

       4.連通子圖:如果G的子圖是連通圖,則稱該子圖為連通子圖。

       5.連通分量與最大連通子圖:無向圖G的最大連通子圖稱為G的連通分量;如果G1是G的連通子圖,將G的任何不在該子圖中的點加入到G1後,G1將不再連通,那麼就稱G1是G的最大連通子圖。

       6.強連通圖:在有向圖中,如果任意兩點之間可相互到達,則稱該圖為強連通圖。(強連通圖與連通圖的區別就在於有向圖與無向圖)

       7.強連通子圖:如果有向圖G的子圖G1是強連通圖,則稱G1為G的強連通子圖

       8.強連通分量與最大強連通子圖:有向圖G的最大強連通子圖稱為G的強連通分量;如果G1是G的強連通子圖,將G的任何不在該子圖的點加入到G1後,G1將不再連通,那麼就稱G1是G的最大強連通子圖。

       然後,理清楚上面的一些概念之後我們就開始來講講如何求出一個圖強連通分量的個數(由於我們只學習如何求出一個圖強連通分量的個數,所以似乎只需要理解什麼是強連通分量即可?)

       也許到了這裡,大家鬆了一口氣說終於沒有需要記憶的概念了。如果這麼想,那就錯了。接下來我們還要介紹一個圖中的點的DFN值與LOW值

       先搬出DFN與LOW的概念:我們用DFS來遍歷一個有向圖,在遍歷的過程中,用DFN[I]表示編號為I的節點在DFS過程中的訪問序號,用LOW[I]表示節點I及其下方節點(也就是有向邊的終點節點)所能到達的訪問序號最小的節點的訪問序號,顯然初始DFN[I]=LOW[I]。我們在通過DFS遍歷的過程中顯然會得到一棵搜尋樹。在搜尋樹上越上層的節點,顯然DFN的值就越小。遍歷過程中如果發現某節點在原圖中有邊連到搜尋樹中自己的祖先節點,就更新LOW值。

       沒有懂?那我們來看一下下面的例子。

       比如這一張圖:

      我們不妨以a為起點開始遍歷,於是得到下面的搜尋樹。

       在搜尋樹圖中紫色的數字就是每個點的訪問順序,也就是對應的DFN值,那麼LOW值呢?

       為了加深對於LOW值的理解,我們來簡單模擬一下LOW值的更新:

       初始化:LOW[I]=I;

       我們從1(a,b,...,h分別對應1,2,...,8)開始搜,1的有向邊的終點2,顯然LOW[2]>LOW[1],所以不更新LOW[1],同理從2搜尋到3不更新,一直搜尋到4的有向邊的終點5,這個時候會發現我們並沒有更新任何的LOW值,但是接下來問題就來了。我們根據原圖發現5有一條有向邊終點是3,這個時候我們發現5的這條有向邊連向了自己的某一個最先節點,根據我們上面已經介紹的,就需要更新LOW[5]了,也就是說此時LOW[5]=min(LOW[5],LOW[3])=LOW[3]=3,由於5能夠通過圖中的一條返祖邊到達3,而4可以到達5,於是我們也要更新LOW[4],也就是LOW[4]=min(LOW[4],LOW[5])=LOW[5]=3。這個時候我們就返回到了3號點,發現從3號點出發的有向邊已經討論完了,那麼這個時候就順序返回到自己的一個沒有討論完所有有向邊的祖先節點,也就是1號點了。此時1還存在以1為起點的有向邊沒有討論完,我們順次討論即可。

      此時,我們就已經分析完了整個通過DFS遍歷圖的過程了,得到如下的LOW值:

      LOW[1]=1,LOW[2]=2,LOW[3]=LOW[4]=LOW[5]=3,LOW[6]=1,LOW[7]=LOW[8]=1

      於是我們這個時候花了相當精力在DFN值與LOW值上,那麼這兩個值有什麼用呢?當然是用來幫助我們求目標強連通分量!

      DFN值的簡單應用:在一個有根樹上,怎樣用O(1)的時間判斷一個結點X是否是另一個結點Y的祖先?我們只需要從根節點出發通過DFS遍歷整棵樹,並且在遍歷的過程中記錄每個節點的DFN值和兒子節點數量就可以了。假設X有K個兒子節點,顯然如果滿足這個條件,那麼X就是Y的祖先節點:

      通過上述的介紹,相信不難得出如下結論:

      如果一個節點的LOW值小於DFN值,那麼就說明它或其子孫節點有邊連到自己上方的節點

      如果一個節點的LOW值等於DFN值,那麼久說明它下方的節點不能走到它上方的節點,也就是說那麼該節點就是一個強連通分量在DFS搜尋樹中的根。

      那麼問題就來了: X的子孫節點未必與X處於同一個強連通分量,那麼應該如何找到在X的子孫節點中,有哪些點是與X位於同一個強連通分量當中呢?對於這個問題我們用棧解決就行了。

給出Tarjan求強連通分量的程式碼:

//Num記錄已經討論到了幾個點。
//Scc記錄已經找到的強連通分量的個數
//Ins[I]表示I點是否在棧中,即是否正被討論當中
//Bel[I]表示I點屬於哪一個強連通分量 
stack<LL>S;
void Tarjan(LL X){
	DFN[X]=LOW[X]=++Num;
	S.push(X);Ins[X]=true;
	for(LL Y=1;Y<=N;Y++){
		if(Map[X][Y]&&DFN[Y]==0){
			Tarjan(Y);
			LOW[X]=min(LOW[X],LOW[Y]);
		} else if(Map[X][Y]&&DFN[Y]!=0&&Ins[Y]){
			LOW[X]=min(LOW[X],LOW[Y]);
		}
	} 
	if(DFN[X]==LOW[X]){
        ++SCC;  
        Y=S.top();S.pop();Bel[Y]=SCC;  
        while(X!=Y){  
            Y=S.top();S.pop();Bel[Y]=SCC;  
        }  
    }  
}

       但是僅僅是這樣的做法其實不能保證討論完每一個點,因為原圖不一定是一個連通圖,這個時候我們就需要一個呼叫視窗:

for(LL I=1;I<=N;I++){
    if(!Bel[I]){
        Tarjan(I);
    }
}

       至此,我們就講完了如何用Tarjan來求出一個圖的SCC個數以及每個SCC都有哪些點(什麼?你什麼時候講了Tarjan?其實上面整個DFN與LOW相關都是Tarjan演算法的一部分)。

       上述如有錯誤,恭請請教!

相關推薦

學習Tarjan連通分量

       正題之前嘮嘮幾句,寫這篇部落格的原因是因為做到了一道圖論題,發現顯然需要縮點解決,但是發現自己圖論中的縮點基本忘完了,同時與之相關的Tarjan求SCC(強連通分量Strong Connected Component)也不記得多少,才想著把圖論當中的Tarjan

Tarjan算法連通分量

HR 得出 強連通 遍歷 str 時間復雜度 滿足 ack ID 轉自:byvoid:有向圖強連通分量的Tarjan算法 Tarjan算法是基於對圖深度優先搜索的算法,每個強連通分量為搜索樹中的一棵子樹。搜索時,把當前搜索樹中未處理的節點加入一個堆棧,回溯時可以判斷棧頂到

Tarjan連通分量 BZOJ 5201 —— Connections

對於每一個節點,我們保留一條樹邊,以及最多一條返祖邊.注意這條返祖邊要指向儘可能高的位置.這樣下來保留的邊數一定小於等於2∗n2*n2∗n,並且滿足圖依舊是強連通的.至於為什麼,貪心的想一想.既然之前

圖論算法-Tarjan模板 縮點;割頂;雙連通分量

else if false -m 例如 als for 算法思路 連通 tarjan 圖論算法-Tarjan模板 【縮點;割頂;雙連通分量】 為小夥伴們總結的Tarjan三大算法 Tarjan縮點(求強連通分量) int n; int low[100010],dfn[1

[NOIP2017] 逛公園 最短路連通分量

size def con tar 出現 typedef esp pair string 題目分析: 首先考慮無數條的情況。出現這種情況一定是一條合法路徑經過了$ 0 $環中的點。那麽預先判出$ 0 $環中的點和其與$ 1 $和$ n $的距離。加起來若離最短路徑不超過$ k

2017 烏魯木齊賽區網路賽 Islands(連通問題縮點+點連通分量

題目連結:https://nanti.jisuanke.com/t/16955 【中文題意】給你一個有向圖,然後讓你加最少的邊使得全圖強連通(即從任意一個點出發,可以到達剩餘的所有點)。 【思路分析】非常經典的板子題。先找強連通分量,然後縮點後求邊的條數。

Tarjan相關(連通分量,割點,縮點)

一、先上模板QWQ #include<bits/stdc++.h>//tarjan-強連通分量 using namespace std; const int MAXM=100005; const int MAXN=5005; int n,m,tot,head[MAXN],dfn[MAXN],l

HDU1269迷宮城堡------------------用Tarjan演算法計算連通分量

Problem Description  為了訓練小希的方向感,Gardon建立了一座大城堡,裡面有N個房間(N<=10000)和M條通道(M<=100000),每個通道都是單向的,就是說若稱某通道連通了A房間和B房間,只說明可以通過這個通道由A房間到達B房間,但並不說明通過它

Tarjan演算法-求連通分量入門

Tarjan演算法求強連通分量,強連通分量就是有向圖(可以是子圖)中的任意兩點都能互相到達,所以我們可以用Tarjan演算法去求出所有的強連通分量,相當於縮點,然後把這些縮點連線起來,就是DAG(有向無環圖),DAG一定有一個點是出度為0的,可以嘗試畫畫圖。#include

Kosaraju與Tarjan(圖的連通分量)

map lse one soft 後續遍歷 所有 要求 基於 圖論 Kosaraju 這個算法是用來求解圖的強連通分量的,這個是圖論的一些知識,前段時間沒有學,這幾天在補坑... 強連通分量: 有向圖中,盡可能多的若幹頂點組成的子圖中,這些頂點都是相互可到達的,則這些

20行程式碼實現,使用Tarjan演算法求解連通分量

今天是演算法資料結構專題的第36篇文章,我們一起來繼續聊聊強連通分量分解的演算法。 在上一篇文章當中我們分享了強連通分量分解的一個經典演算法Kosaraju演算法,它的核心原理是通過將圖翻轉,以及兩次遞迴來實現。今天介紹的演算法名叫Tarjan,同樣是一個很奇怪的名字,奇怪就對了,這也是以人名命名的。和Kos

算法Tarjan算法求連通分量

標記 我們 else if show tar ans stk oid 入棧 概念: 在有向圖G中,如果兩個定點u可以到達v,並且v也可以到達u,那麽我們稱這兩個定點強連通。 如果有向圖G的任意兩個頂點都是強連通的,那麽我們稱G是一個強連通圖。 一個有向圖中的最大強連通子圖

迷宮城堡 tarjan連通分量的個數

題目連結:HDOJ-1269 ## **題目描述**: 為了訓練小希的方向感,Gardon建立了一座大城堡,裡面有N個房間(N<=10000)和M條通道(M<=100000),每個通道都是單向的,就是說若稱某通道連通了A房間和B房間,只說明可以通過這個通道由A房間到達B房

演算法模板Tarjan連通分量

#include<iostream> #include<cstring> #include<stack> #include<vector> using namespace std; const int MAXN=1000+1

hdu 4685 Prince and Princess 匈牙利演算法-匹配、連通分量-Tarjan-縮點

Prince and Princess Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 65535/32768 K (Java

圖論練習-有向圖的連通分量tarjan

這周剛剛看了圖論的一些東西,感覺自己理解比較費勁,所以這裡小小總結一下,如果有誤,歡迎指出 好了,現在我們來看一下圖論的一些基礎的概念: 有向圖強連通分量:在有向圖G中,如果兩個頂點vi,vj間(vi>vj)有一條從vi到vj的有向路徑,同時還有一條從

連通分量縮點拓撲排序dp預處理CDOJ1640 花自飄零水自流,一種相思,兩處閑愁。

如果 vector brush algo blog pri cmp 處理 ret 題意: 在n個點m條邊的有向圖上,從1出發的回路最多經過多少個不同的點 可以在一條邊上逆行一次 題解: 在同一個強連通分量中,顯然可以經過當中的每一個點 因此先將強連通分量縮點,點權為強連通分

poj2553The Bottom of a Graph(連通分量縮點)

targe ring sin spa const ostream 連通 stream pty 題目鏈接:http://poj.org/problem?id=2553 【題意】 給n個點m條邊構成一幅圖,求出所有的sink點並按順序輸出。sink點是指該點能到達的點反過來

訓練題連通分量縮點 P1679

Description 有 N 個人和每個人所認識人的列表,注意:即使B在A的列表中,A也不一定在B的列表中。現在小明有一個重要訊息要通知這N個人,注意:如果A認識B,則當A得到這個訊息,他就會立即通知B。 現在請你完成下面兩個任務: 任務1:請你計算要讓N個人都得到訊息,那麼小明必須把這個訊息直接

hdu4635 Strongly connected 計算連通分量+縮點+思想

題目連結:http://acm.hdu.edu.cn/showproblem.php?pid=4635 題意:給一個有向圖,求最多新增幾條邊讓原圖還不是強連通; 思路:試想一下,如果是個強連通圖,那麼就只有一個強連通分量,所以根據題意讓圖不是強連通那麼至少有兩個強連通分量; 所以設兩個強