东方博宜OJ 3015:强连通分量统计 ← Kosaraju 算法

【题目来源】
https://oj.czos.cn/p/3015

【题目描述】
有一个 n 个点,m 条边的有向图,请求出这个图点数大于 1 的强连通分量个数。

【输入格式】
第一行为两个整数 n 和 m。 第二行至 m+1 行,每一行有两个整数 a 和 b,表示有一条从 a 到 b 的有向边。​​​​​​​

【输出格式】
仅一行,表示点数大于 1 的强联通分量个数。​​​​​​​

【输入样例】
9 20
1 6
9 6
3 4
4 3
5 6
6 7
7 8
5 1
9 5
8 7
5 9
9 8
9 1
8 1
1 5
6 1
8 5
1 9
7 5
7 6​​​​​​​

【输出样例】
2

【数据范围】
对于全部的测试点,保证 2≤n≤10^4,2≤m≤5×10^4,1≤a,b≤n。

【算法分析】
本题代码是利用 Kosaraju 算法求解强连通分量个数的模板题。
Kosaraju 算法是一种用于查找有向图中强连通分量(Strongly Connected Components, SCC)的经典算法。它的核心思想是通过两次深度优先搜索(DFS)来实现,时间复杂度为 O(V+E),其中 V 是顶点数,E 是边数。

● 算法步骤
(一)第一次 DFS(正向图)
对原始有向图进行 DFS 遍历,记录每个顶点的完成时间(即退出递归的顺序)。
将顶点按完成时间的逆序压入一个栈中(完成时间越晚的顶点越早入栈)。
(二)转置图
构建原图的转置图(将所有边的方向反转)。
(三)第二次 DFS(反向图)
从栈顶依次弹出顶点,对转置图进行 DFS。
每次 DFS 访问到的所有顶点构成一个强连通分量。

● 为什么有效?
第一次 DFS 确定了顶点的拓扑顺序(基于完成时间)。
转置图将原图中的强连通分量内部的环保持不变,但改变了不同分量之间的连接方向。
第二次 DFS 在转置图上按逆序访问,可以确保每次只探索同一个强连通分量内的顶点,不会跨到其他分量。

【算法代码】

#include<bits/stdc++.h>
using namespace std;

const int N=1e5+5;
vector<int> G[N],G2[N];
vector<int> group[N];
int st[N];
int post[N],post_cnt;
int scc[N],scc_cnt;

void dfs1(int u) {
    st[u]=1;
    for(int v:G[u]) {
        if(!st[v]) dfs1(v);
    }
    post[++post_cnt]=u;
}

void dfs2(int u) {
    st[u]=1;
    scc[u]=scc_cnt;
    for(int v:G2[u]) {
        if(!st[v]) dfs2(v);
    }
}

void kosaraju(int n) {
    memset(st,0,sizeof st);
    post_cnt=0;
    for(int i=1; i<=n; i++) {
        if(!st[i]) dfs1(i);
    }

    memset(st,0,sizeof st);
    scc_cnt=0;
    for(int i=post_cnt; i>=1; i--) {
        int u=post[i];
        if(!st[u]) {
            ++scc_cnt;
            dfs2(u);
        }
    }
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    int n,m;
    cin>>n>>m;
    for(int i=1; i<=m; i++) {
        int x,y;
        cin>>x>>y;
        G[x].push_back(y);
        G2[y].push_back(x);
    }

    kosaraju(n);

    for(int i=1; i<=n; i++) {
        group[scc[i]].push_back(i);
    }

    int ans=0;
    for(int id=1; id<=scc_cnt; id++) {
        if(group[id].size()>1) ans++;
    }
    cout<<ans<<endl;

    return 0;
}

/*
in:
9 20
1 6
9 6
3 4
4 3
5 6
6 7
7 8
5 1
9 5
8 7
5 9
9 8
9 1
8 1
1 5
6 1
8 5
1 9
7 5
7 6

out:
2
*/



【参考文献】
https://blog.csdn.net/hnjzsyjyj/article/details/164631569

posted @ 2026-09-09 09:16  Triwa  阅读(6)  评论(0)    收藏  举报