給定一個有向圖,要求尋找全部強聯通分量。
首先要先明確幾點:
1.點a和點b連通 當且僅當 存在邊(a,b)和(b,a)
2.將一個有向圖的所有強連通分量看成頂點,則該圖是有向無環圖(dag)。
如下圖:
還有明確幾點性質:
1.對一個"聚點"(強連通分量)的點進行DFS,則不會跑出這個連通分量。因為沒有邊可以出去。(如上圖的h連通分量)
2.DFS后,post值最高的為源點
所以我們的思路可以是這樣,找到聚點(某個點),DFS它,由性質1可知,能被訪問的點組成的就是一個強連通分量。刪除它(因為是dag,所以必定會有新的聚點),再找聚點……
如何找到聚點呢?我們可以先把圖G的邊取反,記為GT,找到GT的源點,則為G的聚點,源點的尋找方法由性質2得到。
所以算法如下:
1.對G進行DFS,更新post數組。
2.計算GT。
3.對GT進行DFS,不過要按照post值遞減的順序進行explore。
代碼貼上

#include <iostream> #include <vector> #include <algorithm> using namespace std; #define Max 100 int n, m; //n為點數,m為邊數 int clk; //用於更新pre post int pre[Max], post[Max]; bool vis[Max]; vector<int> v[Max]; //鄰接表 vector<int> v2[Max]; int post2[Max]; pair<int, int> p[Max];//用於得到post2 bool cmp(pair<int, int> a, pair<int, int> b) { return a.second >= b.second; } //更新post2,按post遞減的順序存post的下標 void sortPost() { for (int i = 1; i <= n; ++i) { p[i].first = i; p[i].second = post[i]; } sort(p+1, p+n+1, cmp); for (int i = 1; i <= n; ++i) post2[i] = p[i].first; } //對G中的邊取反 void G_T() { for (int i = 1; i <= n; ++i) { for (int j = 0; j < v[i].size(); ++j) { v2[v[i][j]].push_back(i); } } } void post_vis(int x) { post[x] = clk; clk++; } void explore(int x) { vis[x] = true; for (int i = 0; i < v[x].size(); ++i) { if (!vis[v[x][i]]) { explore(v[x][i]); } } post_vis(x); } void dfs() { //init clk = 1; for (int i = 1; i <= n; ++i) vis[i] = false; //explore for (int i = 1; i <= n; ++i) if (!vis[i]) explore(i); //explore次數為連通分量次數 } void output() { cout << endl; } void explore2(int x) { vis[x] = true; for (int i = 0; i < v2[x].size(); ++i) if (!vis[v2[x][i]]) explore2(v2[x][i]); cout << x << ' '; } void dfs2() { //init for (int i = 1; i <= n; ++i) vis[i] = false; sortPost(); //explore 按post遞減順序 for (int i = 1; i <= n; ++i) { if (!vis[post2[i]]) { explore2(post2[i]); output();//到這里得到一個強連通分量 } } } int main() { cin >> n >> m; int a,b; for (int i = 0; i < m; ++i) { cin >> a >> b; v[a].push_back(b); } dfs(); G_T(); dfs2(); system("pause"); }
測試數據
8 14
1 2 2 3 3 4 4 3
5 1 2 5 2 6 3 7 4 8
5 6 6 7 7 6 7 8 8 8