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

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

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

#include 
#include 
#include 
#include 
#include 
#include 

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\n", i, scc_no[i]);
    }
    //每个scc包含几个点
    cout << endl << "-------------------" << endl;
    for(int i = 1; i <= SCC; ++i){
        printf("SCC = %d   cnt = %d\n", 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("\n");
    }   
    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;
}

 

你可能感兴趣的:(tarjan算法 有向图求强连通分量模板)