關於強連通分量

2021-08-09 20:39:01 字數 3872 閱讀 8762

首先要明白什麼是強連通分量?

強連通分量實際上指的是一些點的集合,而這個集合的定義就是:任意集合中的點都能到達其他所有同樣在集合中的點

然後就是強連通分量的意義,或者說是作用:

就目前而言,我認為他的作用就是縮點。

什麼叫做縮點呢?

就是當把整個圖中的點分成多個不同的集合以後,每個集合都可以看成乙個點。然後每個集合之間的連線就像這些超級點的連線一樣。

為什麼要縮點?

這有點像物理中的整體法,就是當這些集合中的點具有等價性,或者說可以看成乙個整體的時候就可以縮點了(這幾句話比較繞,而且沒什麼卵用。。。具體方法可以看例題)

然後先是強連通分量的求法:

(先貼的是我自己腦補的板子,目測應該是正確的)

#include#include#include#include#includeusing namespace std;

const int maxn = 1e3 + 5;

bool vis[maxn] = ;

int n, m;

int dfs[maxn] = , dfn[maxn] = , low[maxn] = , num[maxn] = ;

int cnt = 0, tnt = 0;

vectoredge[maxn];

stacks;

inline void putit() }

void tarjan(int t)

for(int i = 0; i < edge[t].size(); ++i)

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

printf("\n"); }}

inline void print()

inline void workk()

int main()

這個板子應該可以直接用吧。

例題:t1:

在幻想鄉,上白澤慧音是以知識淵博聞名的老師。春雪異變導致人間之里的很多道路都被大雪堵塞,使有的學生不能順利地到達慧音所在的村莊。因此慧音決定換乙個能夠聚集最多人數的村莊作為新的教學地點。人間之里由n個村莊(編號為1..n)和m條道路組成,道路分為兩種一種為單向通行的,一種為雙向通行的,分別用1和2來標記。如果存在由村莊a到達村莊b的通路,那麼我們認為可以從村莊a到達村莊b,記為(a,b)。當(a,b)和(b,a)同時滿足時,我們認為a,b是絕對連通的,記為。絕對連通區域是指乙個村莊的集合,在這個集合中任意兩個村莊x,y都滿足。現在你的任務是,找出最大的絕對連通區域,並將這個絕對連通區域的村莊按編號依次輸出。若存在兩個最大的,輸出字典序最小的,比如當存在1,3,4和2,5,6這兩個最大連通區域時,輸出的是1,3,4。

輸入格式:

第1行:兩個正整數n,m

第2..m+1行:每行三個正整數a,b,t, t = 1表示存在從村莊a到b的單向道路,t = 2表示村莊a,b之間存在雙向通行的道路。保證每條道路只出現一次。

輸出格式:

第1行: 1個整數,表示最大的絕對連通區域包含的村莊個數。

第2行:若干個整數,依次輸出最大的絕對連通區域所包含的村莊編號。

對於60%的資料:n <= 200且m <= 10,000

對於100%的資料:n <= 5,000且m <= 50,000

5 5

1 2 1

1 3 2

2 4 2

5 1 2

3 5 1

3
1 3 5

顯然一看就是一道板子題,為什麼要貼呢?

因為我這次的板子是用的是藍書上的板子(道理和我基本上差不多)

(ps :我並沒有排字典序,因為洛谷資料很水的嘛~)

貼**:

#include#include#include#include#includeusing namespace std;

const int maxn = 5e3 + 5;

int pre[maxn] = , lowlink[maxn] = , sccno[maxn] = , dfs_clock = 0, scc_cnt = 0;

int num[maxn] = ;

int n, m, maxx = 0, ans = 0;

vectoredge[maxn];

stacks;

inline void putit()

}void tarjan(int t)

else if(sccno[now] == 0)

}if(lowlink[t] == pre[t])

// printf("%d %d ", scc_cnt, num[scc_cnt]);

if(maxx < num[scc_cnt])

maxx = num[scc_cnt], ans = scc_cnt;

//printf("maxx == %d\n", maxx);

// printf("%d maxx == %d\n", num[scc_cnt], maxx); }}

inline void workk()

printf("%d\n", maxx);

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

}int main()

t2:

每頭奶牛都夢想成為牛棚裡的明星。被所有奶牛喜歡的奶牛就是一頭明星奶牛。所有奶

歡b,b喜歡c,那麼a也喜歡c。牛欄裡共有n 頭奶牛,給定一些奶牛之間的愛慕關係,請你

算出有多少頭奶牛可以當明星。

輸入格式:

第一行:兩個用空格分開的整數:n和m

第二行到第m + 1行:每行兩個用空格分開的整數:a和b,表示a喜歡b

輸出格式:

第一行:單獨乙個整數,表示明星奶牛的數量

只有 3 號奶牛可以做明星

【資料範圍】

10%的資料n<=20, m<=50

30%的資料n<=1000,m<=20000

70%的資料n<=5000,m<=50000

100%的資料n<=10000,m<=50000

3 3

1 22 1

2 3

1

基本上求了強連通分量後在一頓瞎操作就好了呀(操作方法真的太多了,大家可以奇思妙想一下)

**奉上:

#include#include#include#include#include#includeusing namespace std;

const int maxn = 1e5 + 5;

int n, m;

int cnt = 0, tnt = 0;

int lin, mm = 0;

int tar[maxn] = ;

int num[maxn] = , low[maxn] = , dfn[maxn] = ;

bool vis[maxn] = ;

bool flag[maxn] = ;

vectoredge1[maxn];

vectorzero;

stacks;

setss[maxn];

inline void putit()

}void tarjan(int t)

for(int i = 0; i < edge1[t].size(); ++i)

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

//printf("\n");

}}inline void workk()}}

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

printf("%d ", tar[lin]);

}int main()

tarjan的初步學習應該就這樣了吧,以後再進行高階。。。

強連通分量 tarjan求強連通分量

雙dfs方法就是正dfs掃一遍,然後將邊反向dfs掃一遍。挑戰程式設計 上有說明。雙dfs 1 include 2 include 3 include 4 include 5 6using namespace std 7const int maxn 1e4 5 8 vector g maxn 圖的鄰...

強連通分量

對於有向圖的乙個頂點集,如果從這個頂點集的任何一點出發都可以到達該頂點集的其餘各個頂點,那麼該頂點集稱為該有向圖的乙個強連通分量。有向連通圖的全部頂點組成乙個強連通分量。我們可以利用tarjan演算法求強連通分量。define n 1000 struct edge e 100000 int ec,p...

強連通分量

在有向圖g中,如果兩個頂點間至少存在一條路徑,稱兩個頂點強連通 strongly connected 如果有向圖g的每兩個頂點都強連通,稱g是乙個強連通圖。非強連通圖有向圖的極大強連通子圖,稱為強連通分量 strongly connected components 下圖中,子圖為乙個強連通分量,因為...