Tarjan演算法整理

2022-02-17 18:55:59 字數 4079 閱讀 1709

眾所周知,tarjan是個非常nb的人,他發明了很多nb的演算法,tarjan演算法就是其中乙個,它常用於求解強連通分量,割點和橋等。雖然具體實現的細節不太一樣,但是大體思路是差不多的。先來說一下大體思路。

強連通分量,縮點

我們先來定義幾個東西

時間戳:在搜尋樹中被遍歷到的次序

比如在下圖中

每個節點按照遍歷順序編的號就是它的時間戳

dfn[i]:表示第i個點的時間戳

low[i]:表示點i及i的子樹所能追溯到的最早的節點的時間戳

low陣列看起來很難理解是不是?

先來看一張非常經典的圖

我們發現對於結點1,3,2,4,它們的low值都是1。為什麼呢?因為這些點都直接或者間接的能夠追溯到的最早的點1,而點1的dfn值為1,所以這些點的low值自然也就是1了

我們可以通過手算發現圖中有三個強連通分量:,,

我們發現,每乙個連通分量都有乙個點(以下稱為代表點)的low值=dfn值,也就是說這個點及它的子樹所能到達的最早的點就是他自己。

於是可以知道,對於dfn=low的點就是這乙個強連通分量的代表點

那麼要求強連通分量,實際上就是求有多少個點的low=dfn

用乙個棧來實現,尋找low時只在棧裡面找,彈出時不斷從棧頂彈出直到彈出這個點

**:

int dfn[50005],low[50005

];//

dfn表示時間戳

//low表示點i及i的子樹所能追溯到的最早的節點的時間戳

intind;

//ind表示遍歷順序

intin[50005],s[50005

],top;

//in表示當前這個點是否在佇列中

//s是模擬的棧

//top是棧頂

intcnt_scc;

//強連通分量的個數

int scc[50005],cntscc[50005

];//

scc表示每乙個點屬於哪乙個強連通分量

//cntscc表示強連通分量的大小

void tarjan(int

x)

else}}

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

如果找到強連通分量的代表點 }}

來看幾道例題:

如果有環,意味著這個環裡的牛都互相喜歡

我們可以先求出環,然後把每乙個環都看作乙個點,這樣整個圖就變成了乙個dag(有向無環圖)

看有幾個點出度為0,如果大於乙個點沒有出邊,就說明沒有最受歡迎的牛,因為必定有一對牛相互不服

如果只有乙個,那麼強聯通分量的大小就是答案

**:

#includeusing

namespace

std;

intn,m;

int cnt,head[50000

];struct

edge

edg[

50005

];inline

void add(int

from,int

to)int dfn[50005],low[50005],ind,in[50005

];int s[50005

],top;

intcnt_scc;

int scc[50005],cntscc[50005

];void tarjan(int

x)

else}}

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

}}int

out[50005

];int

ans;

intmain()

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

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

}for(int i=1;i<=cnt_scc;i++)}}

cout

<

}

p2002 訊息傳遞

我們發現如果這個題有環,那麼不論在這個環上哪乙個點開始傳遞資訊,這個環中其他的點都可以到達,那麼可以用tarjan把環縮成點。為了使每乙個點都能被傳遞到,只需要找到所有入度為0的點,在這些點上開始傳遞資訊就好了

**:

#includeusing

namespace

std;

intn,m;

int head[100005

],cnt;

struct

edge

edg[

500005

];inline

void add(int

from,int

to)

int low[100005],dfn[100005

],ind;

int s[100005

],top;

bool

in[100005

];int scc[100005

],cnt_scc;

inline

void tarjan(int

x)

else

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

}}int

ans;

int gin[100005

];int

main()

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

for(int i=1;i<=n;i++)}}

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

cout

<

}

類似的題還有洛谷1262,這裡就先不說了

tarjan求割點

什麼是割點?

給你一張連通圖,在上面找乙個點,如果去掉這個點和所有連著它的邊,整個圖就不能保持連通,那麼這個點就是割點

比如這張圖,裡面的割點有1,4,5

怎麼求割點?

首先選定乙個dfs樹的樹根,從這個點開始遍歷整張圖。

對於根節點,判斷是不是割點顯然只需要看他的子樹的個數是不是大於等於2

對於非根節點x,如果存在兒子節點y,使得dfn[x]<=low[y],則x一定是割點。

顯然如果x的所有兒子能夠不經過x直接到達他的祖先,這個點就一定不是割點;反之,則說明去掉它一定會改變圖的連通性

**:

int low[20005],dfn[20005

],ind,ans;

bool cut[20005

];inline

void tarjan(int x,int

fa)

else

}if(x==fa&&ch>=2) cut[fa]=1

;}

例:**:

#includeusing

namespace

std;

intn,m;

int head[20005

],cnt;

struct

edge

edg[

200005

];inline

void add(int

from,int

to)

int low[20005],dfn[20005

],ind,ans;

bool cut[20005

];inline

void tarjan(int x,int

fa)

else

}if(x==fa&&ch>=2) cut[fa]=1;}

intmain()

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

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

cout

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

}

關於tarjan演算法的一些整理

要noip了,來補一些演算法。當然馬蜂也都是很久以前的了,不喜勿噴。定義 顯然僅在有向圖中有 乙個極大子圖滿足內部任意一點出發可到其他任一點。可以用來縮點。code 一些地方有注釋。注意僅有這個 是沒fa的。void tarjan int u if low u dfn u top 定義 這個順便是有...

tarjan演算法詳解

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

Tarjan 演算法筆記

tarjan演算法 tarjan演算法屬於圖論中的乙個演算法,主要用來求乙個圖中的強連通分量,之後就可以做很多事,比如說縮點 求雙聯通分支等。強連通 在乙個有向圖中,對於幾個點,如果它們能夠互相到達,那麼稱它們強連通。強連通分量 可以這樣理解 把乙個圖里的點分成幾坨,每坨中的點都能夠互相到達 他們強...