📌  相关文章
📜  强连通分量(1)

📅  最后修改于: 2023-12-03 15:39:32.550000             🧑  作者: Mango

强连通分量

在图论中,我们把可以通过有向边从一个顶点到达另一个顶点的集合称为强连通分量(Strongly Connected Component,简称SCC)。

在实际应用中,我们可以利用强连通分量把复杂的问题分解成多个较简单的子问题,从而更好的进行分析。

强连通分量的算法

Tarjan算法是最先提出求解强连通分量的算法之一,效率较高,被广泛应用。若图有N个点,M条边,则Tarjan算法的时间复杂度为O(N+M)。

Tarjan算法思路

基本思路:深度优先遍历,并用某种方式维护每个节点所在的强连通分量信息。

基本步骤:

1.从某个没有被访问过的节点开始,利用DFS对图进行遍历。

2.维护一个栈s,每访问一个节点,就把该节点压入栈s中。同时,记录该节点的访问顺序dfn[i],和该节点所能到达的最小dfn值low[i]。(详见代码实现)

3.在每次回溯的时候,若当前节点i的low值等于dfn值,则从栈s中pop出以i为栈底的各个节点,这些节点组成的集合即为一个强连通分量。(详见代码实现)

Tarjan算法实现:
1. 建立图的类
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
using namespace std;

const int MAXN = 10005;
const int MAXM = 100005;

int n, m;
int head[MAXN], cnt;
struct Edge{
    int to, nxt;
}edge[MAXM];

void addEdge(int u, int v){
    edge[++cnt] = (Edge){v, head[u]};
    head[u] = cnt; 
}
2. Tarjan算法实现
stack<int> s;
int dfn[MAXN], low[MAXN], vis[MAXN];
int tot, scc[MAXN];

void Tarjan(int u){
    dfn[u] = low[u] = ++tot;
    vis[u] = 1;
    s.push(u);
    for(int i = head[u]; i; i = edge[i].nxt){
        int v = edge[i].to;
        if(!dfn[v]){
            Tarjan(v);
            low[u] = min(low[u], low[v]);
        }
        else if(vis[v]){
            low[u] = min(low[u], dfn[v]);
        }
    }
    if(dfn[u] == low[u]){
        int v;
        do{
            v = s.top(); s.pop();
            vis[v] = 0;
            scc[v] = tot;
        }while(u != v);
        tot++;
    }
}
3. 主函数以及输出结果
int main(){
    scanf("%d%d", &n, &m);
    int u, v;
    for(int i = 1; i <= m; i++){
        scanf("%d%d", &u, &v);
        addEdge(u, v);
    }
    for(int i = 1; i <= n; i++){
        if(!dfn[i]){
            Tarjan(i);
        }
    }
    printf("%d\n", tot - 1);
    for(int i = 1; i <= n; i++){
        printf("%d ", scc[i]);
    }
    printf("\n");
    return 0;
}

其中,打印出的tot-1即为强连通分量的数量。scc[i]存储了每个节点所在的强连通分量编号。

总结

强连通分量是图论中重要的概念,Tarjan算法是求解强连通分量的高效算法之一。掌握Tarjan算法对于理解和实现图论算法有重要意义。