圖的遍歷(dfs)
對於有向圖g中的任意兩個頂點u和v存在u->v的一條路徑,同時也存在v->u的路徑,我們則稱這兩個頂點強連通。以此類推,強連通分量就是某乙個分量內各個頂點之間互相連通。
簡單來說,就是有向圖內的乙個分量,其中的任意兩個點之家可以互相到達。
求有向圖內部強連通分量的方法大概有2種:tarjan演算法,korasaju演算法。這裡我們只對tarjan演算法進行討論。
tarjan演算法是tarjan神仙提出的基於dfs時間戳和堆疊的演算法,這裡我們可以先來看一下什麼是dfs時間戳
dfs時間戳就是dfs的先後順序,詳細來講,比如我們dfs最先訪問到的節點是a,於是a的時間戳就是1,第二個訪問到的節點是e,那麼e的時間戳就是2,我們用\(dfn[u]\)來表示u節點的時間戳,應該算是比較簡單的
首先,除了dfn以外我們還需要乙個low陣列,這個陣列記錄了某個點通過圖上的邊能回溯到的dfn值最小的節點。這句話相信在大多數部落格裡面都有提到,這裡我們來看乙個簡單的例子:
首先,我們有乙個圖g:
假設我們從a點出發開始dfs,我們可以畫出乙個dfs樹:
為什麼我們畫出來的dfs樹和原來的圖不一樣呢?因為我們在dfs的過程中實際上是會忽略某一些連線到已訪問節點的邊的,這些邊我們暫且稱之為回邊。對於點u來說,\(low[u]\)儲存的就是點u通過某一條(或者是幾條)回邊能到達的dfn值最小的節點(也就是被最先訪問的節點)。假設這個dfn值最小的節點是u',我們可以知道,因為u和u'都是在一棵dfs樹上的,並且u'可以到達u,同時u可以通過一條或多條回邊到達u',也就是說u'->u路徑上的任意節點都可以通過這一條回邊來互相到達,也就是說他們會形成乙個強連通分量。
我們有乙個新圖g:
假設我們從a點出發開始dfs,一路跑到d點,那麼我們為這個圖上的每乙個點加上dfn陣列和low陣列的值(dfn,low),整個圖就會長成這個樣子:
此時我們會遇到一條d->a的回邊,也就是說點d能訪問到的dfn值最小的節點從點d本身變化到了a點,所以點d的low值就會發生相應的變化,\(low[d]=min(low[d],dfn[a])\)。
緊接著,dfs發生回溯,我們沿著之前的路徑逐步更新路徑上節點的low值,於是就有\(low[c]=min(low[c],low[d])\),直到更新到某乙個dfn值和low值相同的節點。因為這個節點能訪問到的最小dfn的節點就是其本身,也就是說這個節點是整個scc最先被訪問到的節點。
全部搞完大概會變成這個樣子:
我們用乙個輔助棧來儲存dfs的路徑,這樣就可以在找到乙個強連通分量裡面最早被訪問到的節點的時候可以輸出路徑。同時因為dfs訪問是一條路走到黑的,所以可以保證棧內在節點u(low[u]==dfn[u])之前的的節點都是屬於同乙個scc的。
還是上面這幅圖,我們順便把e點給更新了:
跑完e點之後就會發現,e點本身的low就是和dfn相等的,所以此時棧內也只有e這乙個節點。
於是上面這個圖的scc有以下幾個:
[e][a,b,c,d]
首先我們要發現,在dfs的初期我們每乙個節點的low和dfn都是相同的,也就是說有dfn[u]=low[u]=++cnt(cnt為計數變數),並且在回溯的過程中要用後訪問節點的low值來更新先訪問節點的low值,也就是說有\(low[u]=min(low[u],low[v])\),當訪問到某乙個在棧中的節點的時候,我們要用這個節點的dfn值來更新其他節點,所以有\(low[u]=min(low[u],dfn[v])\)。
那麼我們乙個簡單的**就可以寫出來了:
void tarjan(int u)
s.pop();
//這裡pop掉的就是一開始的那個low[u]==dfn[u]的節點。因為相關資訊已經維護完畢,所以這裡直接pop也沒問題
}
把這兩部分結合在一起,就是tarjan求scc的完整**了:
void tarjan(int u)
};vectorgpe[maxn];
int dfn[maxn],low[maxn],ins[maxn],scc[maxn],size[maxn],cnt=0,sccn=0;
stacks;
void tarjan(int u){
dfn[u]=low[u]=++cnt;
s.push(u);
ins[u]=1;
for(int i=0;i**以前寫的,略冗長,見諒
題目推薦:
真·模板題: p2863 [usaco06jan]the cow prom s
p1262 間諜網路
p2746 [usaco5.3]校園網network of schools
Tarjan演算法 縮點
我們這一篇是在已經了解tarjan演算法的基礎之上開始寫的,如果不了解的話,請先看大牛們 關於tarjan演算法的部落格。首先我們對於乙個有向無環的圖 dag 至少新增幾條邊才能使它變為強連通圖?我們很容易根據有向無環圖的性質得到,我們計算入度為零的點數為a,出度為零的點數為b,那麼我們至少需要新增...
tarjan縮點 求橋模板
這麼一看縮點用tarjan也沒必要啊,用之前那個存反向邊dfs兩次的做法就行了 縮點過程就是遍歷邊,兩側不同scc就加邊 include pragma comment linker,stack 1024000000,1024000000 include include include include...
Tarjan縮點 SPFA 縮點
洛谷p3387縮點 tarjan spfa求dag上單源最短路模板題 用tarjan在原圖上求scc 縮點 用縮點之後的scc建乙個有向無環圖 scc權為此scc內所有點點權和 在新建的dag上將scc權視為邊權跑spfa 求scc 1 到scc n 的最長路即為所求答案 include inclu...