看了許多部落格,還是這個最清楚:強連通演算法–tarjan個人理解+詳解
牆裂安利大家看看上面的部落格!講的非常清楚!(但是文章的**好像有一點點小錯誤。
tarjan演算法主要解決了有向圖中有幾個強連通分量的問題,基於tarjan演算法,可以對在同一連通分量的點染色,進而縮點,生成有向無環圖。
例:給定有向圖,求:
至少要選幾個頂點,才能做到從這些頂點出發,可以到達全部頂點
至少要加多少條邊,才能使得從任何乙個頂點出發,都能到達全部頂點
思路縮點之後, dag上面有多少個入度為0的頂點,問題1的答案就是多少;在dag上要加幾條邊,才能使得dag變成強連通的,問題2的答案就是多少。
加邊的方法:
假定有 n 個入度為0的點,m個出度為0的點,如何加邊?附上**:把所有入度為0的點編號 0,1,2,3,4 …n -1
每次為乙個編號為i的入度0點可達的出度0點,新增一條出邊,連到編號為(i+1)%n 的那個入度0點,這需要加n條邊。
若 m <= n,則加了這n條邊後,已經沒有入度0點,則問題解決,一共加了n條邊
若 m > n,則還有m-n個入度0點,則從這些點以外任取一點,和這些點都連上邊,即可,這還需加m-n條邊。
所以,max(m,n)就是第二個問題的解
#include
//**僅供參考
#include
#include
#include
#include
using namespace std;
#define maxn 1000000
struct eedge[maxn]
;int head[maxn]
,k;int vis[maxn]
;int dfn[maxn]
,low[maxn]
,cnt,sig,color[maxn]
;int in_cnt,out_cnt,in[maxn]
,out[maxn]
;int n,m;
stack<
int>s;
void
init()
void
add(
int u,
int v)
void
tarjan
(int u)
else
if(vis[v]==1
)}if(dfn[u]
== low[u]
)while(1
);printf
("\n");
}}void
slove()
printf
("強連通分量共%d個\n"
,sig);}
//1) 至少要選幾個頂點,才能做到從這些頂點出發,可以到達全部頂點
//2) 至少要加多少條邊,才能使得從任何乙個頂點出發,都能到達全部頂點
void
scc(
)else}}
for(
int i =
1; i <= sig;
++i)
printf
("至少要選 %d 個頂點,才能從這些頂點出發,可達全部頂點\n至少要加 %d 條邊,使得從任何乙個頂點出發,到達全部頂點\n"
,in_cnt,
max(in_cnt,out_cnt));
}}intmain()
slove()
;for
(int i =
1; i <= n;
++i)
scc();
}}
一組測試用例
強連通圖縮點的應用:最小點基
題目大意:
給你n個炸彈,對應已知其座標和**範圍,以及引爆這個炸彈需要的花費,對應如果引爆了炸彈a,沒有引**彈b,但是b炸彈在a炸彈的作用範圍之內,那麼b炸彈也會被引爆,問將所有炸彈都引爆需要的最小花費。
思路:把可以引爆的炸彈間連邊,建立有向圖;如果有環,使用tarjan演算法縮點染色(同環內可以相互引爆);找到入度為0的分量,找出其中花費最小的點作為引爆這個分量及其相連分量的點累加。
綜上所述,tarjan強連通縮點染色之後,找到度為0的節點,並且在其中找到花費最小的炸彈,累加即可。
具體的:在dfn[u] == low[v]時更新分量的最小花費
關於最小點基和最小權點基:最小點基,最小權點基(summer holiday hdu - 1827)(強連通分量的應用)
Tarjan演算法 縮點
我們這一篇是在已經了解tarjan演算法的基礎之上開始寫的,如果不了解的話,請先看大牛們 關於tarjan演算法的部落格。首先我們對於乙個有向無環的圖 dag 至少新增幾條邊才能使它變為強連通圖?我們很容易根據有向無環圖的性質得到,我們計算入度為零的點數為a,出度為零的點數為b,那麼我們至少需要新增...
tarjan演算法求scc 縮點
圖的遍歷 dfs 對於有向圖g中的任意兩個頂點u和v存在u v的一條路徑,同時也存在v u的路徑,我們則稱這兩個頂點強連通。以此類推,強連通分量就是某乙個分量內各個頂點之間互相連通。簡單來說,就是有向圖內的乙個分量,其中的任意兩個點之家可以互相到達。求有向圖內部強連通分量的方法大概有2種 tarja...
強連通分量及縮點tarjan演算法解析
再次深搜 此時 stack 發現綠邊指向了已經遍歷過的點4 是上述的2種邊之一 而4在棧中 4點與6點是父子關係 該邊為後向邊 4 6的路徑上的點都是環。int num n top 0 int u stack.top while u 4 num top u 如此就能把stack中 4 6路徑上的點轉...