第七课 寻找强连通分量

给定一个有向图,要求寻找全部强联通分量。

首先要先明确几点:

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

原文地址:https://www.cnblogs.com/chenyg32/p/3061186.html