• 第七课 寻找强连通分量


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

    首先要先明确几点:

    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

  • 相关阅读:
    iOS开发UI篇—控制器的View的创建
    iOS开发UI篇—控制器的创建
    OS开发UI篇—UIWindow简单介绍
    iOS开发UI篇—使用storyboard创建导航控制器以及控制器的生命周期
    A1089. Insert or Merge
    A1044. Shopping in Mars
    A1010. Radix
    A1085. Perfect Sequence
    散列、贪心总结
    A1038. Recover the Smallest Number
  • 原文地址:https://www.cnblogs.com/chenyg32/p/3061186.html
Copyright © 2020-2023  润新知