第七課 尋找強連通分量


給定一個有向圖,要求尋找全部強聯通分量。

首先要先明確幾點:

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。

 

代碼貼上

View Code
#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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM