"Tarjan" strongly connected component Tarjan seek template

Notice the premise of learning Tarjan

Tarjan is able to find a strongly connected components algorithm. What is a strong link? In that figure, the two can reach each other to form a ring, we call this ring is a strong link, which in this figure can ring up composition points, we call strongly connected component, and our Tarjan can communication with the strong and strong even Unicom component is reduced in a series of operations points

Algorithms content

Competition point need to use

1, the high degree of freedom Tarjan obtain strong link, and is not recommended tied strongly connected components

2, Tarjan more common, the composition can be considered a model to use

Tarjan and strong component Unicom speak a little

It low Tarjan algorithm understanding is not deep, there is no one blog to make specific explanation on the wiki references here

Tarjan algorithm wiki

Tarjan algorithm

Robert E. Tarjan (1948 ~) American.

Many algorithms invention Tarjan structure. Tarjan algorithm Tarjan algorithm, a lot of light, such as various communication components required Tarjan algorithm seeking LCA (Lowest Common Ancestor, common ancestor) a. Disjoint-set, Splay, Toptree invention also Tarjan.

Here it is to introduce in the strongly connected components are asking to Tarjan algorithm of FIG.

In addition, Tarjan's name jis not pronounced, translated into Chinese Yang tower.

DFS spanning tree

Before introducing the algorithm, first to understand the DFS spanning tree , we have to map the following example:

There are four kinds of DFS spanning tree to the side of the main (not necessarily all appear):

  1. Tree edges (tree edge): green side, each search to find a node has not visited when he formed a side of the tree.
  2. Anti progenitor edge (back edge): yellow edge, also called the back side, i.e. the edge pointing ancestor node.
  3. Cross fork side (cross edge): red edge, it is mainly encountered when searching a node already visited, but this node is not formed when the ancestors of the current node.
  4. 前向边(forward edge):蓝色边,它是在搜索的时候遇到子树中的结点的时候形成的。

我们考虑 DFS 生成树与强连通分量之间的关系。

如果结点 \(u\) 是某个强连通分量在搜索树中遇到的第一个结点,那么这个强连通分量的其余结点肯定是在搜索树中以 \(u\) 为根的子树中。\(u\) 被称为这个强连通分量的根。

反证法:假设有个结点 \(v\) 在该强连通分量中但是不在以 \(u\) 为根的子树中,那么 \(v\)\(u\) 的路径中肯定有一条离开子树的边。但是这样的边只可能是横叉边或者反祖边,然而这两条边都要求指向的结点已经被访问过了,这就和 \(u\) 是第一个访问的结点矛盾了。得证。

Tarjan 算法求强连通分量

在 Tarjan 算法中为每个结点 \(u\) 维护了以下几个变量:

  1. \(dfn[n]\) :深度优先搜索遍历时结点 u 被搜索的次序。
  2. \(low[u]\) :设以 u 为根的子树为 \(Subtree(u)\)\(low[u]\) 定义为以下结点的 \(dfn\) 的最小值: \(Subtree(u)\) 中的结点;从 \(Subtree(u)\) 通过一条不在搜索树上的边能到达的结点。

一个结点的子树内结点的 dfn 都大于该结点的 dfn。

从根开始的一条路径上的 dfn 严格递增,low 严格非降。

按照深度优先搜索算法搜索的次序对图中所有的结点进行搜索。在搜索过程中,对于结点 和与其相邻的结点 \(v\) (v 不是 u 的父节点)考虑 3 种情况:

  1. \(v\) 未被访问:继续对 v 进行深度搜索。在回溯过程中,用 \(low[v]\) 更新 \(low[u]\) 。因为存在从 \(u\)\(v\) 的直接路径,所以 \(v\) 能够回溯到的已经在栈中的结点,\(u\) 也一定能够回溯到。
  2. \(v\) 被访问过,已经在栈中:即已经被访问过,根据 \(low\) 值的定义(能够回溯到的最早的已经在栈中的结点),则用 \(dfn[v]\) 更新 \(low[u]\)
  3. \(v\) 被访问过,已不在在栈中:说明 \(v\) 已搜索完毕,其所在连通分量已被处理,所以不用对其做操作。

对于一个连通分量图,我们很容易想到,在该连通图中有且仅有一个 \(dfn[u] = low[u]\) 。该结点一定是在深度遍历的过程中,该连通分量中第一个被访问过的结点,因为它的 DFN 值和 LOW 值最小,不会被该连通分量中的其他结点所影响。

因此,在回溯的过程中,判定 \(dfn[u] = low[u]\) 的条件是否成立,如果成立,则栈中从 \(u\) 后面的结点构成一个 SCC。

实现代码如下 参考LuoGuP2341强连通分量模板

[此代码未编译 可能会有问题 请斟酌参考]

//#define fre yes

#include <cstdio>
#include <cstring>
#include <iostream>

const int N = 50005;
int low[N], dfn[N];
int head[N << 1], to[N << 1], ver[N << 1];
int color[N], Stack[N], de[N];
bool Vis[N];

int tot;
void addedge(int x, int y) {
    ver[tot] = y;
    to[tot] = head[x];
    head[x] = tot++;
}

int num, top, col;
void tarjan(int x) {
    dfn[x] = low[x] = ++num;
    Stack[++top] = x;
    Vis[x] = 1;
    for (int i = head[x]; ~i; i = to[i]) {
        int v = ver[i];
        if(!dfn[v]) {
            tarjan(v);
            low[x] = std::min(low[x], low[v]);
        } else if(Vis[v]) {
            low[x] = std::min(low[x], dfn[v]);
        }
    }
    
    if(dfn[x] == low[x]) {
        ++col;
        color[x] = col;
        Vis[x] = 0;
        while(Stack[top] != x) {
            color[Stack[top]] = col;
            Vis[Stack[top--]] = 0;
        } top--;
    }
}

int main() {
    memset(head, -1, sizeof(head));
    static int n, m;
    scanf("%d %d", &n, &m);
    for (int i = 1; i <= m; i++) {
        int u, v;
        scanf("%d %d", &u, &v);
        addedge(u, v);
    }
    
    for (int i = 1; i <= n; i++) {
        if(!dfn[i]) {
            tarjan(i);
        }
    }
    
    for (int i = 1; i <= n; i++) {
        for (int j = head[i]; ~j; j = to[j]) {
            int v = ver[j];
            if(color[i] != color[v]) {
                de[color[i]]++;
            }
        }
    }

    int ans = 0, u = 0, k = 0;
    for (int i = 1; i <= col; i++) {
        if(!de[i]) {
            u++;
            k = i;
        }
    }

    if(u == 1) {
        for (int i = 1; i <= n; i++) {
            if(color[i] == k) ans++;
        } printf("%d\n", ans);
    } else puts("0");
    return 0;
}

Guess you like

Origin www.cnblogs.com/Nicoppa/p/11492055.html