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 所以我們在回溯的過程中就能夠通過判...