強連通分量 Kosaraju

2022-08-22 15:39:10 字數 3454 閱讀 9486

芝士

有向圖強連通分量在有向圖g中,如果兩個頂點vi,vj間(vi>vj)有一條從vi到vj的有向路徑,同時還有一條從vj到vi的有向路徑,則稱兩個頂點強連通(strongly connected)。如果有向圖g的每兩個頂點都強連通,稱g是乙個強連通圖。有向圖的極大強連通子圖,稱為強連通分量。

如圖中1,2,3,4是乙個強連通分量。

kosaraju演算法:

如果這是乙個無向圖,那麼從乙個節點出發,深搜得到的所有節點都是連通的。

但這是乙個有向圖,起始節點的不同會導致結果的不同,舉個栗子,從5搜可以搜到6,但是從6搜不能搜到5。

這說明需要按照乙個特定的順序深搜。假設1,2,3,4是強連通分量a,5,6分別是強連通分量b,c。a可以搜到b,c,但b,c不能搜到a,由於我們不希望搜到不屬於同乙個強連通分量的點,所以會先搜b,c,再搜a。

那麼這個順序就是被指向的強連通分量要在指向的強連通分量之前被搜到,即被指向的強連通分量中的至少乙個點在指向的強連通分量的任意乙個點之前被搜到。

為了得到這個順序,聰明的kosaraju想到了乙個方法:新建原圖g的逆圖gt(其定義為gt=(v,et),et=}),

按照節點編號順序在gt上深搜,每搜到乙個節點,先把這個節點所能到達的所有未被訪問過的節點加入棧中,再把自己加入棧中,然後按照從棧頂到棧底的順序深搜,這樣保證了在原圖g中能到達我的點,都在我之後被搜到;

最後原圖中強連通分量的個數就等於深搜的次數,每一次深搜到達的未被訪問過的節點屬於乙個強連通分量(可以用乙個陣列記錄一下);

tarjan演算法

從乙個點開始遍歷圖,會得到一棵有向樹,當乙個點有連向其祖先的邊(回邊)時,就會形成環,而連另一棵子樹的邊則不會。

每個點有乙個時間戳t[i],和乙個top[i]表示能反到的最高的點的時間戳,vis[i]表示這個點是否在棧中。

從乙個點開始dfs,遍歷子節點並把它們加入棧中,如果找到了被遍歷且在棧中的點,修改自己的top為那個在棧中的點的時間戳。遍歷完所有子節點後,嘗試用子節點的top更新自己的top,如果自己的top等於自己的t,那麼自己是所在聯通塊裡最高的點,這是從棧中彈出元素一直到彈出自己就找到了自己所在的聯通塊。

如果圖不連通,需要從多個點開始dfs。

模板:

/*

約翰的n (2 <= n <= 10,000)只奶牛非常興奮,因為這是舞會之夜!她們穿上禮服和新鞋子,別上鮮花,她們要表演圓舞.

只有奶牛才能表演這種圓舞.圓舞需要一些繩索和乙個圓形的水池.奶牛們圍在池邊站好, 順時針順序由1到n編號.每只奶牛都面對水池,這樣她就能看到其他的每乙隻奶牛.

為了跳這種圓舞,她們找了 m(2

有的奶牛可能握有很多繩索,也有的奶牛可能一條繩索都沒有.對於乙隻奶牛,比如說貝茜,她的圓舞跳得是否成功,可以這樣檢驗:沿著她牽引的繩索, 找到她牽引的奶牛,再沿著這只奶牛

牽引的繩索,又找到乙隻被牽引的奶牛,如此下去,若最終 能回到貝茜,則她的圓舞跳得成功,因為這乙個環上的奶牛可以逆時針牽引而跳起旋轉的圓舞. 如果這樣的檢驗無法完成,那她的

圓舞是不成功的.如果兩隻成功跳圓舞的奶牛有繩索相連,那她們可以同屬乙個組合.給出每一條繩索的描述,請找出,成功跳了圓舞的奶牛有多少個組合?

輸入n,m,接下來m行

*/#include#include#include#include#include#include#define nn 10010

#define mm 100010

using namespace std;

int e=0,l=0,ee=0,cir;

int nx[mm],fi[nn],too[mm];

int fir[nn],nxt[mm],to[mm],li[nn];

bool vis[nn];

void add(int u,int v)

void add2(int u,int v)

void dfs(int s)

li[++l]=s;

}void dfs2(int s)

}int main()

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

if(!vis[i])

dfs(i);

fill(vis,vis+n+1,0);

for(int i=l;i>=1;i--)

if(!vis[li[i]]) //寫成了vis[i]

printf("%d",sum);

return 0;

}

/*洛谷3387

給定乙個n個點m條邊有向圖,每個點有乙個權值,求一條路徑,使路徑經過的點權值之和最大。你只需要求出這個權值和。

允許多次經過一條邊或者乙個點,但是,重複經過的點,權值只計算一次。

*/#include#include#include//

#include#include#include#define nn 10010

#define mm 100010

using namespace std;

int e=0,ee=0,time=0,la=0,var=0,head=1,tail=0;

int fir[nn],fi[nn],nxt[mm],nx[mm],to[mm],too[mm],q[nn],t[nn],top[nn],stack[nn],w[nn],d[nn],be[nn],dis[nn],in[nn];

bool vis[nn];

int read()

while(isdigit(ch))

return ans*f;

}void add(int u,int v)

void ad(int u,int v)

void tarjan(int s)

else if(vis[to[i]])

top[s]=min(top[s],t[to[i]]); //

if(t[s]!=top[s]) //最高的點才找出這個聯通分量

return;

var++;

dowhile(stack[la+1]!=s); //la+1

}int dp()

int main()

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

if(!t[i])

tarjan(i);

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

for(int j=fir[i];j;j=nxt[j])

if(be[i]!=be[to[j]])

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

if(!in[i])

printf("%d",dp());}/*

5 62 5 4 1 3

1 22 3

2 44 5

5 34 1

*/

強連通分量 Kosaraju演算法

求有向圖的強連通分量除了大家熟知的trajan,還可以用kosaraju 先說演算法流程 1,對原圖dfs一遍,並將出棧順序的逆序作為 偽拓撲序 2,對原圖夠構反向圖 3,按偽拓撲序在反向圖上dfs,新遍歷到的點都屬於同乙個強聯通分量。正確性證明 s在反向圖上dfs能夠遍歷到t,說明存在t到s的路徑...

強連通分量Kosaraju演算法

參考閱讀 如何理解kosaraju演算法?簡緻的回答 知乎 kosaraju演算法步驟 用dfs後序遍歷原圖的反轉圖,得到後序遍歷順序 用得到的後序遍歷順序對原圖dfs 實現 棧和圖的定義 const int maxv 100 定義棧 typedef struct snode stack 定義邊 t...

Kosaraju演算法 強連通分量

有向圖的極大強連通子圖,稱為強連通分量。子圖指的是選取v的乙個子集v 以及e當中所有滿足u,v v 的邊集e 所指代的圖.我們需要找出一幅有向圖當中的所有強連通分量。乙個最樸素的演算法 構造乙個傳遞閉包 也就是陣列aij表示i能否到達j 然後把aij aji 1的節點置於同乙個強連通分量當中 這個演...