Tarjan演算法詳解

2021-08-10 01:54:44 字數 3908 閱讀 5798

tarjan是用來解決圖的割邊割點問題以及有向圖的強連通分量(縮點)的問題的。

割邊是圖論演算法中一類很常見的問題:

在乙個連通圖g中,假設有一條邊e,去掉e後圖g不再連通,那麼e就是g的一條割邊。換句話說,g是連通圖,g-e不是連通圖。

最暴力最暴力的演算法就是每次都去掉一條邊,然後進行dfs深度優先遍歷。要進行n次dfs深度優先遍歷。這顯然效率是很低很低的。

這個時候乙個叫tarjan的男人站了出來。

tarjan演算法是以dfs深度優先遍歷演算法為基礎的。也就是說tarjan是對dfs的乙個最優性剪枝。

在tarjan中,我們需要對每乙個頂點維護兩個值,dfn值和low值。

我們在dfs深度優先遍歷的時候根據dfs遍歷的順序可以畫出一顆dfs樹(有n-1條邊,這n-1條邊稱為樹邊,其餘稱為非樹邊)。我們dfs遍歷的順序編號就是dfn值,我們可以這樣理解:dfn值就是乙個時間戳,dfn[i]=1,就代表第i號結點是第1個被遍歷到的。low值是某個節點通過非樹邊能夠回溯到的dfn值最小的點。比如說low[8]=4;我們就能知道,8號點能通過非樹邊回溯到4號點。

這有什麼用呢?

我們任意連線dfn上的一條非樹邊,發現有這樣乙個性質:總是祖孫結點相連的。這樣的邊稱為返祖邊。我們可以知道,如果乙個點有返祖邊與dfn值更小的點相連,那麼它的父親結點肯定不是割點(好好思考下,想通了再往下面看)。所以我們可以得出乙個結論:在圖中,如果存在一條樹邊(x,y),而且x是y的父親,存在low[y]>dfn[x],那麼(x,y)是割邊。

luogu炸鐵路

這道題目的大意就是要炸掉一條鐵路,讓這個交通系統變成兩部分,這明顯是乙個裸的割邊題目。我們這邊拿這道題目作為乙個模板題來講解。

#include

#define maxn 200

#define maxm 5200*2

using namespace std;

inline int read()

//快讀

/*這個**風格……我已經被大佬們帶掉了

怎麼說呢,就是使用namespace

這個東西有啥好處呢,就是可以使整個**的

結構變得很清晰

不信你看

*/namespace graph//這部分是跟圖論就有關係了

a[maxm];//鄰接表

void insert(int x,int y)

//在鄰接表中插入邊

void init()

}//讀入

}using namespace graph;//一定別忘了寫這句

namespace tarjan//接下來是tarjan

ans[maxm];//這是用來儲存答案的乙個邊表

int dfn[maxn],low[maxn],tot=0,num;

void hy(int u,int fa)

//dfs函式,u代表當前結點,fa代表當前結點的父親

else

//如果被訪問過了

low[u]=min(low[u],dfn[v]);

//那就用v的dfn值更新u的low值

if(dfn[u]//這裡是判斷是否是割邊,這是}}

}using namespace tarjan;

bool mycmp(edge a,edge b)

割點是圖論演算法中另外乙個非常常見的問題。

模擬割邊的定義,割點就是,在連通圖g中,去掉了點a,讓這個圖不再連通,那麼就是割點。也就是g是連通圖,g-a不是連通圖。

這類題目的暴力演算法一樣,就是把所有跟這個點有關係的邊全部去掉,再dfs。太慢了。

tarjan求割點的方法和求割邊的方式類同,都需要維護兩個值:low值和dfn值。意義也基本相同。我們仍然構造一顆dfs樹,如果乙個點有乙個兒子能通過非樹邊到達其他非子孫結點,那麼這個點就不是割點。我們可以說,如果存在一條邊(x,y),x是y的父親。如果low[y]<=dfn[x]那麼說明x是割點。

需要一提的是,由於有向圖都是單向邊,所以非樹邊不一定是返祖邊。可以連到兄**樹的結點的邊也是可以讓這個結點不是割點的。最後要強調的是,只要有乙個兒子結點滿足上面的關係,那麼這個點就是割點。因為至少有乙個點被他阻斷了呀。

思考一下根節點,根節點要怎麼樣才能是割點呢:度》=2

[扭曲鑼鼓 割點模板題(

裸模板題!!tarjan上。

#include

#define maxn 100100

using

namespace

std;

inline

int read()

//快讀

/*還是那個套路。namespace

*/namespace graph

a[maxn*2];//鄰接表

void insert(int x,int y)

//插入

void init()

}//讀入資料

}using

namespace graph;

namespace tarjan//tarjan演算法

low[x]=min(low[x],dfn[y]);

// 用y的dfn值更新x的low值

}if(x==father[x]&°ree>=2)

ans[x]=true;

//如果它是根節點,而且度數》=2,那麼它是割點

}}using

namespace tarjan;

int main()

強連通分量是有向圖中的一類很常見的問題。弄得我痛不欲生!!!

有向圖中乙個連通分量如果滿足這樣的性質:其中任意兩個點都是能夠互相通過路徑到達的,而且整個分量沒有出度,那麼這個連通分量就是強連通分量。

我給恩撒。

我才不寫暴力呢。

tarjan求強連通分量是需要乙個棧來維護乙個被遍歷到的序列,這要碰到dfn[i]=low[i]的就把所有的它以上進棧的全部彈出,這就是個強連通分量了。這個推導的話我推薦乙個blog

大佬部落格。關於強連通分量的證明

luogu訊息擴散

不多說了

統計強連通分量個數。縮點。統計入度為0的強連通分量。

#include

#define maxn 100100

#define maxm 500200*2

using

namespace

std;

inline

int read()

namespace graph

a[maxm];

bool tr[maxm];

struct edge

e[maxm];

void insert(int x,int y)

void init()

}}using

namespace graph;

namespace tarjan

else

if(dfn[y]if(dfn[x]==low[x])

while(x!=r);

/*跟上面的**不一樣對的地方,

如果碰到乙個low和dfn相等的點,

就彈出所有的棧中比它後入棧的點作為

乙個強連通分量

*/ }

}}using

namespace tarjan;

int main()

int ans=0;

for(int i=1;i<=m;i++)

}printf("%d",number-ans);

return

0; }

tarjan演算法詳解

參考 tarjan演算法在強連通分量分離中運用很廣,書寫簡單,並且可以拓展到圖的割點,割邊上,十分強大 具體思路 令dfn u 表示當前點的時間戳 low u 表示當前點所能到達的點的時間戳中最小的乙個 到達點u時,將其入棧 拓展點u後代 當且僅當dfn u low u 時,棧頂元素全部出棧,此時出...

Tarjan演算法詳解

tarjan演算法的用途 1.求橋和割點 2.求點和邊的雙連通分量 3.求強連通 targan演算法的流程 利用dfs來遍歷圖來構建一種數型的結構 tarjan演算法的兩個核心陣列 1 對於第一種用途 tarjan演算法原理 我們從1開始遍歷,發現6,5,4的low不小於dfn 3 故3為割點 即4...

Tarjan演算法詳解

在有向圖g中,如果兩個頂點間至少存在一條路徑,稱兩個頂點強連通 strongly connected 如果有向圖g的每兩個頂點都強連通,稱g是乙個強連通圖。非強連通圖有向圖的極大強連通子圖,稱為強連通分量 strongly connected components 所以我們在回溯的過程中就能夠通過判...