• tarjan算法 有向图求强连通分量模板


    强连通:在有向图中,如果Vx能到达Vy,且Vy也能到达Vx,说明它们两个点强连通。

    强连通分量:在有向图中,存在一个极大子图,该子图中任意两点都是强连通。

    #include <iostream>
    #include <vector>
    #include <cstdio>
    #include <cstring>
    #include <queue>
    #include <map>
    
    using namespace std;
    
    #define ll long long
    #define pb push_back
    #define fi first
    #define se second
    
    const int N = 1e4 + 10;
    int dfn[N]; //时间戳
    int low[N]; //能到达最早点
    int s[N]; //
    int ins[N]; //是否在栈内
    int scc_no[N];//属于哪个scc
    int scc_cnt[N]; //该scc有几个点
    vector<int > scc[N];//缩点
    vector<int > E[N]; //
    vector<int > new_E[N];//缩点后的图
    int SCC; //第几个scc
    int tim;//dfs序号
    int top;//栈顶
    int n, m; //点数  边数
    
    
    //时间复杂度  O(m)
    
    void init(int n){
        for(int i = 1; i <= n; ++i){
            dfn[i] = low[i] = ins[i] = 0;
            scc_cnt[i] = 0;
            scc_no[i] = 0;
            scc[i].clear();
            E[i].clear();
            new_E[i].clear();
        }
        SCC = tim = top = 0;
    }
    
    void tarjan(int now){
        dfn[now] = low[now] = ++tim;
        ins[now] = 1; s[top++] = now;
    
        for(auto to : E[now]){
            if(!dfn[to]){
                tarjan(to);
                low[now] = min(low[now], low[to]);
            }else if(ins[to]){
                //侧边,形成环
                low[now] = min(low[now], dfn[to]);
            }
        }
    
        if(dfn[now] == low[now]){
            int sum = 0; //该scc的点数
            ++SCC;//第几个scc
            int tmp;//暂存点
    
            do{
                tmp = s[--top];//取点
                ins[tmp] = 0;//出栈
                ++sum;//点数++
                scc_no[tmp] = SCC;//属于哪个scc
                scc[SCC].pb(tmp);//放入这个连通图,缩点
            }while(tmp != now);
            scc_cnt[SCC] = sum;//该scc的点数
        }
    }
    
    
    void show_info(){
        //每个点属于哪个scc
        cout << endl << "-------------------" << endl;
        for(int i = 1; i <= n; ++i){
            printf("id = %d  scc_no = %d
    ", i, scc_no[i]);
        }
        //每个scc包含几个点
        cout << endl << "-------------------" << endl;
        for(int i = 1; i <= SCC; ++i){
            printf("SCC = %d   cnt = %d
    ", i, scc_cnt[i]);
            
            //这个scc中点的信息
            printf("have point : ");
            for(auto poi : scc[i]){
                printf("(%d) ", poi);
            }
            cout << endl << endl;
        }
        cout << endl << "-------------------" << endl;
        //新图的边
        for(int i = 1; i <= SCC; ++i){
            printf("id = %d: to : ", i);
            for(auto v : new_E[i]){
                printf("(%d) ", v);
            }printf("
    ");
        }   
        cout << endl << "-------------------" << endl;
    }
    
    void solve(){
    
        while(~scanf("%d%d", &n, &m)){
            //scanf("%d%d", &n, &m);
            //初始化每组数据
            init(n);
            
            //读边
            for(int i = 1; i <= m; ++i){
                int u, v;
                scanf("%d%d", &u, &v);
                E[u].pb(v);
            }
    
            //可能不是一个连通图
            for(int now = 1; now <= n; ++now){
                if(!dfn[now]){
                    tarjan(now);
                }
            }
    
            //新图建边
            for(int now = 1; now <= n; ++now){
                for(auto to : E[now]){
                    if(scc_no[now] != scc_no[to]){
                        new_E[scc_no[now]].pb(scc_no[to]);
                    }
                }
            }
    
            //tarjan()过后的信息展示
            show_info();
    
        }
    }
    
    int main(){
        
        solve();    
        //cout << "not error" << endl;
        return 0;
    }
  • 相关阅读:
    leetcode刷题 557~
    Unity正交相机智能包围物体(组)方案
    Embedded Browser(ZFBrowser)使用相关问题
    Unity性能优化
    sipp的使用
    Elment清除表单验证,防止报错小技巧
    git常用命令(干货)
    文本单词one-hot编码
    86. 分隔链表 链表
    5638. 吃苹果的最大数目 优先队列
  • 原文地址:https://www.cnblogs.com/SSummerZzz/p/13029927.html
Copyright © 2020-2023  润新知