Tarjan算法求强连通图

先把参考资料的传送门放一下:

1、优质的B站视频讲解
(里面老师给出了一个很不错的代码模板)

2、tarjan求强连通分量+缩点+割点/割桥(点双/边双)以及一些证明(原理简述)


先不论缩点是怎么做的,
根据第一个B站视频里的模板,
先用线性的邻接表维护有向图,
基本实现一下Tarjan算法。

const int maxn = 1e5 + 10;

//线性邻接表维护有向图

class Node{
public:
    int nd,nxt;
    Node(){};
    Node(int nd_,int nxt_):nd(nd_),nxt(nxt_){};
};

Node G[maxn];
int head[maxn];
int cnt;

void add(int u,int v){
    G[cnt] = Node(v,head[u]);
    head[u] = cnt++;
}

void init_G(){
    memset(head,-1,sizeof(head));
    cnt = 0;
}

//下面是Tarjan算法求强连通图
int Low[maxn];
int Dfn[maxn];
int num = 0;//编号变量-赋值不能是0
int out[maxn];//标记被栈弹出过的元素
int Stack[maxn];//模拟栈
int cur = 0;//cur应当始终指向Stack的尾端元素

int ans = 0;

/*我们假设结点的编号从1开始*/
int dfs(int u){
    Low[u] = Dfn[u] = ++num;//结点编号
    Stack[++cur] = u;//结点入栈
    //下面开始遍历u的临界结点
    for(int i=head[u];~i;i=G[i].nxt){
        int v = G[i].nd;
        if(Dfn[v] == 0){//v没有被访问
            dfs(v);
            Low[u] = min(Low[u],Low[v]);
        }else if(!out[v]){//v已经被访问且还在栈中
            Low[u] = Low[v];//形成环
        }
    }
    //输出强连通分量
    if(Low[u] == Dfn[u]){
        int cur_num = 0;
        do{
            cur_num++;
            out[Stack[cur]]=1;
        }while(Stack[cur--] != u);
        //这里做的处理是为了统计点个数大于1的连通区块的总个数ans
        if(cur_num>1)ans++;
    }
    return 0;
}

int main(){
    frein("in.txt");
    int n,m;
    cin>>n>>m;
    init_G();
    for(int i=0;i<m;i++){
        int a,b;
        cin>>a>>b;
        add(a,b);
    }
    //由于图并不一定是弱连通图
    //所以这里对每一个没有访问过的结点都进行搜索
    //当然,一般这并不会让复杂度达到O(n*n)
    for(int i=1;i<=n;i++){
        if(!Dfn[i]){
            dfs(i);
        }
    }
    cout<<ans<<endl;
    return 0;
}

OK

原文地址:https://www.cnblogs.com/savennist/p/13887259.html