图论---有向图的强连通分量(Tarjan求SCC)

发布于:2025-05-07 ⋅ 阅读:(43) ⋅ 点赞:(0)

强连通分量作用:有向图——>(缩点)有向无环图(DAG)

缩点:将所有连通的分量缩成一个点。

有向无环图作用/好处:求最短路/最长路 可以递推,按照拓扑图从前往后递推.

x 是否在某个强连通分量中?

情况1:存在后向边指向祖先节点。

情况2:先走到横叉边,横叉边再走到祖先。

时间戳:搜索时按照DFS顺序给每个点一个编号,

对每个点定义两个时间戳:

1、dfn[u] 表示遍历到 u 的时间戳

2、low[u] 从 u 开始走,所能遍历到的最小的时间戳是什么

u 是其所在强连通分量的最高点 等价于 dfn[u] == low[u]

 以下是Tarjan模板

//O(n+m)时间复杂度
//求强连通分量的过程
void tarjan(int u){
    //刚遍历到的时候
    dfn[u]=low[u]=++timestamp;//时间戳
    stk[++top]=u,in_stk[u]=true;
    
    //栈中里面存的所有点都是
    //当前还没有遍历完的强连通分量的所有点
    //在强连通分量中,并且这个强连通分量还没有遍历完
    
    //遍历所有 u 能到的点
    for(int i=h[u];~i;i=ne[i]){
        int j=e[i];
        //u 还没被遍历过
        if(!dfn[j]){
            //遍历一下这个点
            tarjan(j);
            low[u]=min(low[u],low[j]);
        }
        else if(in_stk[j]){
            low[u]=min(low[u],dfn[j]);
        }
    }
    
    //else 里面的 j 要么是祖先要么是横叉点
    
    if(dfn[u]==low[u]){
        int y;
        ++scc_cnt;
        do{
            y=stk[top--];
            in_stk[y]=false;
            id[y]=scc_cnt;
        }while(y!=u);
    }
}

缩点
用邻接表存
遍历所有点,遍历 i 的所有邻点
if(i 和 j 不在同一个SCC中){
     加一条新边 id[i] -> id[j] 存的是i所在的连通分量的编号
}
建成的图是 有向无环图
DAG可以用拓扑排序来做
连通分量编号递减的顺序一定是拓扑序


网站公告

今日签到

点亮在社区的每一天
去签到