• 【图论】强连通分量


    强连通缩点:

    算法复杂度:
    Kosaraju算法:初始化,加边,两次dfs,复杂度O(n+m)
    强连通分量缩点算法:遍历每个点每条边,复杂度O(n+m)
    对边排序去重:复杂度O(n+mlogm)

    注意:
    1、最好先 Init() ,然后再 AddEdge()
    2、维护缩点时点的性质对新点的影响在 dfs2() 中进行
    3、维护缩点时边的性质对新点的影响在 Build() 中进行,特别注意缩点之后的自环
    4、并不是每道题都需要原图反图,也并不是都需要对边进行去重

    Kosaraju算法缩点的结果本身就是按拓扑序排列的。

    好像在不开O2的情况下这个vector版的比链式前向星版的费多了很多时间。

    使用方法:

    Init,传入原图的点数。
    使用AddEdge逐个加入有向边。
    调用Kosaraju划分强连通分量(V2存储强连通缩点后的新点包含原图的哪些点,c2存储原图的点对应强连通缩点后的哪个新点)。
    调用Build在强连通缩点之后的新点之间建立新边到G2,并排序去重。
    在Solve中书写在DAG中求解的代码,例如先把原图的点的信息传递给强连通缩点后的新点,然后在DAG上dp(注意是使用G2)。

    namespace SCC {
        int n;
        vector<int> G[MAXN + 5], BG[MAXN + 5];
    
        int c1[MAXN + 5], cntc1;
        int c2[MAXN + 5], cntc2;
        int s[MAXN + 5], cnts;
    
        int n2;
        vector<int> V2[MAXN + 5];
        vector<int> G2[MAXN + 5], BG2[MAXN + 5];
    
        void Init(int _n) {
            n = _n;
            cntc1 = 0, cntc2 = 0, cnts = 0;
            for(int i = 1; i <= n; ++i) {
                G[i].clear();
                BG[i].clear();
                c1[i] = 0;
                c2[i] = 0;
                s[i] = 0;
                V2[i].clear();
                G2[i].clear();
                BG2[i].clear();
            }
            return;
        }
    
        void AddEdge(int u, int v) {
            G[u].push_back(v);
            BG[v].push_back(u);
            return;
        }
    
        void dfs1(int u) {
            c1[u] = cntc1;
            for(auto &v : G[u]) {
                if(!c1[v])
                    dfs1(v);
            }
            s[++cnts] = u;
        }
    
        void dfs2(int u) {
            V2[cntc2].push_back(u);
            c2[u] = cntc2;
            for(auto &v : BG[u]) {
                if(!c2[v])
                    dfs2(v);
            }
            return;
        }
    
        void Kosaraju() {
            for(int i = 1; i <= n; ++i) {
                if(!c1[i]) {
                    ++cntc1;
                    dfs1(i);
                }
            }
            for(int i = n; i >= 1; --i) {
                if(!c2[s[i]]) {
                    ++cntc2;
                    dfs2(s[i]);
                }
            }
            return;
        }
    
        void Build() {
            n2 = cntc2;
            for(int i = 1; i <= n2; ++i) {
                for(auto &u : V2[i]) {
                    for(auto &v : G[u]) {
                        if(c2[v] != i) {
                            G2[i].push_back(c2[v]);
                            BG2[c2[v]].push_back(i);
                        }
                    }
                }
            }
            for(int i = 1; i <= n2; ++i) {
                sort(G2[i].begin(), G2[i].end());
                G2[i].erase(unique(G2[i].begin(), G2[i].end()), G2[i].end());
                sort(BG2[i].begin(), BG2[i].end());
                BG2[i].erase(unique(BG2[i].begin(), BG2[i].end()), BG2[i].end());
            }
            return;
        }
    
        void Solve() {
            for(int i = 1; i <= n2; ++i) {
                for(auto &u : V2[i]) {
                    //把原图的信息传递给新图;
                }
            }
            //在新图上Solve;
            return;
        }
    }
    

    添加最少的边,使得有向图变成一个强连通图。

    先缩点,假如只剩下一个点就返回。否则,依次配对入度为0和出度为0的点。答案是这两种里面较多的那种。

  • 相关阅读:
    PHP实现大文件下载
    使用CSS样式的三种方式
    PHP工厂模式
    使用 curl 命令发送请求
    vim 基本操作
    MAC OS 各个文件夹详细介绍以及 node 安装位置
    linux find 命令
    Mac 关闭某端口程序
    glob 模式的 Linux Shell 通配符介绍
    Mac tree 输出文件树形式
  • 原文地址:https://www.cnblogs.com/purinliang/p/14091829.html
Copyright © 2020-2023  润新知