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
  • 相关阅读:
    Knockout应用开发指南 第八章:简单应用举例(2)
    微软ASP.NET站点部署指南(7):生产环境部署
    Knockout应用开发指南 第七章:Mapping插件
    《Microsoft Sql server 2008 Internals》读书笔记第九章Plan Caching and Recompilation(6)
    《Microsoft Sql server 2008 Internals》读书笔记第九章Plan Caching and Recompilation(5)
    《Microsoft Sql server 2008 Internals》读书笔记第九章Plan Caching and Recompilation(3)
    《Microsoft Sql server 2008 Internals》读书笔记第九章Plan Caching and Recompilation(9)
    《Microsoft Sql server 2008 Internals》读书笔记第九章Plan Caching and Recompilation(8)
    Microsoft Visual Studio .NET 2003 引导程序插件下载地址(非官方)
    Vs2010在没有安装SQL Server 2005/2008 Express时如何连接MDF数据文件?
  • 原文地址:https://www.cnblogs.com/shuaihui520/p/9642373.html
Copyright © 2011-2022 走看看