浅谈Tarjan求强连通分量

首先,要知道什么是强连通分量,这个还是比较简单的吧。
那么就谈谈Tarjan求强连通分量。

概率(扯淡)

首先,Tarjan算法是基于对图深度优先搜索(DFS)的算法,每个强连通分量为搜索树中的一个子树。
搜索时把当前搜索树中未处理的结点加入一个栈,回溯时可以判断栈顶中的结点是否构成一个强连通分量。
DFS的过程中遇到的四种边:
1、树枝边:DFS时经过的边,即DFS搜索树上的边。
2、前向边:与DFS方向一致,从某个结点指向其某个子孙的边
3、后向边:与DFS方向相反,从某个结点指向其某个祖先的边
4、横叉边:从某个结点指向搜索树中另一个子树中的某个结点的边。
定义DFN(n)为结点u的搜索次序编号(时间戳)可以理解为是第几个dfs到的,low(u)为 u 或u的子树能够追溯到的最早的栈中结点的DFN值。其定义可以得出:
如果(u,v)为树枝边,u 为 v 的父节点,则low( u ) = min(low( u ), low( v ) );
如果(u,v)为后向边或指向栈中结点的横叉边,则low( u ) = min (low( u ),dfn( v ));
然后如果在回溯的时候DFN[u]=low[u] 那么久可以退栈了,就是以u为根的搜索子树上所有还在栈中的结点是一个强连通分量,就是u和在u之后搜索到的可以组成一个强连通分量。

算法流程(乱搞+魔改)

就是先一个for枚举每个点为起点(搜索树的树根)看当前是否被搜索过,没有就进去乱搞一通,然后就可以了(就是利用上面的那两个得出来的东东)

我扔:https://www.luogu.org/problemnew/show/P1726

这题接近于裸题,就多记一个数量就行了
代码:

#include<bits/stdc++.h>
#define N 100050
using namespace std;
struct edge{//链式前向星
    int v,next;
}e[N];
int p[N],eid;
void add(int u,int v){
    eid++;
    e[eid].v=v;
    e[eid].next=p[u];
    p[u]=eid;
}
int dfn[N],low[N],bel[N],sta[N],bl[N],sz,tot,top;
void dfs(int x){ //Tarjan 求强连通分量
    dfn[x]=++sz; 
    low[x]=dfn[x]; //一开始low[x]就是等于dfn[x](暂时追溯到最早的就是自己)
    sta[++top]=x;
    for(int i=p[x];i;i=e[i].next){
        int v=e[i].v;
        if(!dfn[v]){ //第一个得出来的
            dfs(v);
            low[x]=min(low[x],low[v]);
        }
        else
        if(!bel[v]) low[x]=min(low[x],dfn[v]); 
    }
    if(low[x]==dfn[x]){
        bel[x]=++tot;
        bl[tot]++; //记一下强连通分量的大小
        while(sta[top]!=x){
            bel[sta[top]]=tot;
            --top;
            bl[tot]++;
        }   
        --top;  
    }
}
int n,m;
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        int u,v,t;
        scanf("%d%d%d",&u,&v,&t);
        if(t==2) add(v,u);
        add(u,v);
    }
    for(int i=1;i<=n;i++){
        if(!dfn[i]) dfs(i);
    }
    int ma=0,mai=0;
    for(int i=1;i<=tot;i++){
        if(bl[i]>ma) ma=bl[i],mai=i;
    }
    for(int i=1;i<=n;i++){
        if(ma==bl[bel[i]]){
            mai=bel[i];
            break;
        }
    }
    printf("%d\n",ma);
    for(int i=1;i<=n;i++){
        if(bel[i]==mai) printf("%d ",i);
    }
    return 0;
}
//啦啦啦啦(防伪标识)

蛤?就没了
没了……

参考资料:
《信息学奥赛一本通·提高篇》

posted @ 2018-08-15 20:31  lahlah  阅读(25)  评论(0编辑  收藏  举报