如何用Tarjan求强连通分量——洛谷P2863题解

强连通分量

强连通分量就是一个有向图的子集,这个子集中的点任意两点间一定可以互相到达。如下图:

如图上面的红框中的点就组成了一个强连通分量。

如何求强连通分量:

首先我们要有两个数组 dfn[]low[] 。其中 dfn[i] 表示第i个点的在第 dfn[i] 次被dfs到。然后 low[i] 存的是第 \(i\) 个点所在的连通块里面 dfn 的值最小的点。之后我们还需要一个临时存放遍历到但是还没找到其所属的强连通分量的点。然后我们就可以开始我们的算法流程了。

然后上代码:

void Tarjan(int x, int index) {
    dfn[x] = low[x] = index; //初始化dfn, low
    stack_tmp[++tail] = x;   //将当前结点放入栈中
    check[x] = true;             //标记结点x表示x在栈中
    for (register int i = head[x]; i; i = Edge[i].nxt) {
        if (!dfn[Edge[i].e]) Tarjan(Edge[i].e, index + 1);
        low[x] = Min(low[x], low[Edge[i].e]); //更新low值
    }
    if (low[x] == dfn[x]) {
        int sum = 0; //记录连通块内结点的数量
        while (stack_tmp[tail] != x && tail > 0) {
            check[stack_tmp[tail]] = false; //取消标记
            ++sum;
            --tail; //退栈
        }
        if (tail != 0 && stack_tmp[tail] == x) { //将当前结点退栈
            check[stack_tmp[tail]] = false;
            ++sum;
            --tail;
        }
    }
}

也许有人会对下面这段代码不是很理解。

 for (register int i = head[x]; i; i = Edge[i].nxt) {
        if (!dfn[Edge[i].e]) Tarjan(Edge[i].e, index + 1);
        low[x] = Min(low[x], low[Edge[i].e]); //更新low值
    }

这样更新 low值的原因是假如当前结点没有被访问过,那么我们首先一定是要访问这个结点所连向的边,因为我们的 low[x] 的值是从点 \(x\) 连出去的点的 low值更新来的,所以我们要先 dfs 连出去的点,更新连出去的点的 low 值,直到遍历到的点没有连出去的点的时候,那么就说明这个点肯定自己就是一个强连通分量(因为我们不可能通过任何路径回到这个点)。所以不用更新 low 值,直接返回。然后我们就要更新当前结点的 low 值了。然后我们更新当前结点的 low 值就可以了。之后假如 low[x] == dfn[x] 那么说明我们成功找到一个强连通分量,然后将栈中的结点弹出直到将当前结点 \(x\) 弹出为止。

模板题:

Luogu P2863

代码:

#include <iostream>
#include <cstdio>
#include <algorithm>

using namespace std;

const int N = 1e5 + 5;
const int M = 5e4 + 5;

int n, m;
int u, v;
int head[N];
int cnt = 0;
int ans = 0;
int dfn[N], low[N];
int stack_tmp[N], tail = 0;
bool check[N];

inline int Min(int x, int y) {
    return x <= y ? x : y;
}

struct EDGE {
    int s;
    int e;
    int nxt;
} Edge[N];

void add(int u, int v) {
    ++cnt;
    Edge[cnt].s = u;
    Edge[cnt].e = v;
    Edge[cnt].nxt = head[u];
    head[u] = cnt;
}

void Tarjan(int x, int index) {
    dfn[x] = low[x] = index;
    stack_tmp[++tail] = x;
    check[x] = true;
    for (register int i = head[x]; i; i = Edge[i].nxt) {
        if (!dfn[Edge[i].e]) Tarjan(Edge[i].e, index + 1);
        low[x] = Min(low[x], low[Edge[i].e]);
    }
    if (low[x] == dfn[x]) {
        int sum = 0;    
        while (stack_tmp[tail] != x && tail > 0) {
            check[stack_tmp[tail]] = false;
            ++sum;
            --tail;
        }
        if (tail != 0 && stack_tmp[tail] == x) {
            check[stack_tmp[tail]] = false;
            ++sum;
            --tail;
        }
        if (sum > 1) ++ans;
    }
}

int read() {
    int s = 0, w = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-') w = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        s = s * 10 + ch - '0';
        ch = getchar();
    }
    return s * w;
}

void write(int x) {
    if (x < 0) putchar('-'), x = -x;
    if (x > 9) write(x / 10);
    putchar(x % 10 + '0');
}

int main(int argc, char const *argv[]) {
    n = read(), m = read();
    for (register int i = 1; i <= m; ++i) {
        u = read(), v = read();
        add(u, v);
    }
    Tarjan(1, 1);
    for (register int i = 1; i <= n; ++i) 
        if (!dfn[i]) Tarjan(i, 1);
    write(ans), putchar('\n');
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/lixiao189/p/9825322.html