zoukankan      html  css  js  c++  java
  • 求有向强连通分量 模板

    模板

     

    Kosaraju算法

    int V; // 顶点数
    vector<int> G[MAX_V]; // 图的邻接表表示
    vector<int> rG[MAX_V]; // 把边反向后的图
    vector<int> vs; // 后序遍历顺序的顶点列表
    bool used[MAX_V]; // 访问标记
    int cmp[MAX_V]; // 所属强连通分量的拓扑序322 第 4 章 登峰造极——高级篇
    void add_edge(int from, int to)
    {
        G[from].push_back(to);
        rG[to].push_back(from);
    }
    void dfs(int v)
    {
        used[v] = true;
        for (int i = 0; i < G[v].size(); i++)
        {
            if (!used[G[v][i]]) dfs(G[v][i]);
        }
        vs.push_back(v);
    }
    void rdfs(int v, int k)
    {
        used[v] = true;
        cmp[v] = k;
        for (int i = 0; i < rG[v].size(); i++)
        {
            if (!used[rG[v][i]]) rdfs(rG[v][i], k);
        }
    }
    int scc()
    {
        memset(used, 0, sizeof(used));
        vs.clear();
        for (int v = 0; v < V; v++)
        {
            if (!used[v]) dfs(v);
        }
        memset(used, 0, sizeof(used));
        int k = 0;
        for (int i = vs.size() - 1; i >= 0; i--)
        {
            if (!used[vs[i]]) rdfs(vs[i], k++);
        }
        return k;
    }
    View Code

    输出每个编号强连通

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<string>
    #include<vector>
    #include<algorithm>
    #define INF 99999999
    #define MAX_V 10000
    using namespace std;
    int V;
    vector<int>G[MAX_V];
    vector<int>rG[MAX_V];
    vector<int>vs;
    vector<int>SCC[MAX_V];
    bool used[MAX_V];
    int cmp[MAX_V]; 
    void add_edge(int from,int to){
        G[from].push_back(to);
        rG[to].push_back(from);
    }
    void dfs(int v){
        used[v]=true;
        for(int i=0;i<G[v].size();i++){
            if(!used[G[v][i]])dfs(G[v][i]);
        }
        vs.push_back(v);
    } 
    void rdfs(int v,int k){
        used[v]=true;
        cmp[v]=k;
        SCC[k].push_back(v); 
        for(int i=0;i<rG[v].size();i++){
            if(!used[rG[v][i]])rdfs(rG[v][i],k);
        }
    } 
    int scc(){
        memset(used,0,sizeof(used));
        vs.clear();
        for(int i=1;i<V;i++){
            if(!used[i])dfs(i);
        }
        memset(used,0,sizeof(used));
        int k=0;
        for(int i=vs.size()-1;i>=0;i--){
            if(!used[vs[i]])rdfs(vs[i],k++);
        }
        return k;
    }
    void init(){//初始化有向图 
        V=13;
        add_edge(4,1);
        add_edge(2,3);
        add_edge(3,2);
        add_edge(4,3);
        add_edge(6,3);
        add_edge(6,4);
        add_edge(6,5);
        add_edge(5,7);
        add_edge(7,6);
        add_edge(9,7);
        add_edge(9,8);
        add_edge(8,10);
        add_edge(10,9);
        add_edge(11,8);
        add_edge(11,10);
        add_edge(12,11);    
    } 
    int main(){
        init();
        int ccount=scc();
        for(int i=0;i<ccount;i++){
            for(int j=0;j<SCC[i].size();j++){
                if(j==0)printf("连通分量%d:%d",i,SCC[i][j]); 
                else printf(" %d",SCC[i][j]);
            }
            printf("
    ");
        }
        return 0;
    }
    View Code

    tarjan算法

    DFN[ i ] : 在DFS中该节点被搜索的次序(时间戳)

    LOW[ i ] : 为i或i的子树能够追溯到的最早的栈中节点的次序号

    当DFN[ i ]==LOW[ i ]时,为i或i的子树可以构成一个强连通分量。

    void add_edge(int u,int v)
    {
        edge[u].push_back(v);
    }
    void tarjan(int u)
    {
        instack[u] = true;
        low[u] = DFN[u] = ++tot;
        ss.push(u); //将刚访问的节点入栈
        for(int i = 0;i < edge[u].size();i++)
        {
            int v = edge[u][i];
            if(!DFN[v]) //没有被访问过
            {//不能写成不在栈中,因为在栈中的一定是访问过的,但是不在栈中的也可能访问过,只是已经划入之前的强连通分量
                tarjan(v);
                low[u] = min(low[u],low[v]);
            }
            else if(instack[v]) // 指向栈中节点的后向边
            {
                low[u] = min(low[u],DFN[v]);
            }
        }
        if(DFN[u] == low[u]) // u 为一个强连通分量的根
        {
            scc++;//强连通分量的编号
            int v;
            do
            {
                v = ss.top();
                ss.pop();
                belong[v] = scc; //标记每个节点所在的强连通分量
                num[scc]++; //每个强连通分量的节点个数
            }while(u != v);
        }
    }
    View Code

    这个貌似比上面的要好一点

    vector<int> G[maxn];
    int Mark[maxn], Root[maxn], Stack[maxn];                                //时间戳,根(当前分量中时间戳最小的节点),栈
    bool Instack[maxn];                                                     //是否在栈中标记
    int Ssc[maxn];                                                          //每个节点所在的强连通分量的编号
    int Index, Ssc_n, Top;                                                  //搜索时用的时间戳,强连通分量总数,栈顶指针
    
    void Tarjan(int u)                                                      //u 当前搜索到的点
    {
        Mark[u] = Root[u] = ++ Index;                                       //每找到一个点,对时间戳和根初始化
        Stack[Top ++] = u;                                                  //压栈
        Instack[u] = true;                                                  //在栈中标记
    
        int v;
    
        for(int i= 0; i< G[u].size(); i++)                                  //向下搜索
        {
            v = G[u][i];
            if(Mark[v] == 0)                                                //没到过的点
            {
                Tarjan(v);                                                  //先向下搜索
                if(Root[u] > Root[v]) Root[u] = Root[v];                    //更新根
            }
            else if(Instack[v] && Root[u] > Mark[v]) Root[u] = Mark[v];     //到过的点且点仍在栈中,试着看这个点能不能成为根
        }
    /*对当前点的搜索结束*/
        if(Mark[u] == Root[u])                                              //当前点本身时根
        {
            Ssc_n ++;                                                       //更新强连通分量数
    
            do{                                                             //栈中比它后入栈的元素在以它为根的强连通分量中
                v = Stack[-- Top];
                Instack[v] = false;
                Ssc[v] = Ssc_n;
            }while(v != u);                                                 //直到它自己
        }
    }
    
    void SSC()
    {
        memset(Mark, 0, sizeof Mark);                                       //初始化时间戳和栈内标记
        memset(Instack, false, sizeof Instack);
        Index = Ssc_n = Top = 0;                                            //初始化时间戳,强连通分量数,栈顶指针
    
        for(int i= 1; i<= N; i++)                                           //保证图上所有点都访问到
            if(Mark[i] == 0) Tarjan(i);
    }
    /***************************Tarjan算法模板***************************/
    View Code
  • 相关阅读:
    2020牛客暑期多校训练营(第一场)I 1or 2题解
    5-23ACM训练题解(NWERC 2019)
    5-20ACM训练题解(2017-2018 ACM-ICPC Pacific Northwest Regional Contest)
    5-6ACM训练题解(2019 Russia Team Open Contest)
    5-2ACM训练题解(Asia Nakhon Pathom Regional Contest)
    4-30ACM训练题解(ICPC Asia Taipei-Hsinchu Contest)
    4-22ACM训练题解(ZOJ Monthly Jan 2019)
    「学习笔记」子序列自动机
    「学习笔记」后缀自动机
    「学习笔记」后缀数组
  • 原文地址:https://www.cnblogs.com/shuaihui520/p/9642373.html
Copyright © 2011-2022 走看看