[Tarjan] Tarjan详细介绍(顺手写版本)

简介: 敲黑板在Tarjan算法推进的过程中,一些需要定义的数组以及变量,现在首先进行声明一下:dfn[x],表示节点x的时间戳,通俗说就是当前这个点x被访问的次序,第一个被访问的dfn[x]是1,第二个dfn[x] = 2low[x],表示节点x或者是x的子树能够追溯到1的最早的栈中节点的编号;low[x]一开始是dfn[x],然后会不断更新,成为强连通分支子树根节点的dfn,当dfn[x] == low[x]的时候,以x为根的搜索子树上所有的节点是一个强连通分支stack st;实现栈在Tarjan中的作用;pos[x] or colour[x]可以记录节点x所在的强连通分支是第几个

本文章bug可能比较多,也可能讲的不是很完美,所以难免会经过多次修改,如有不恰当,还请各位大佬赐教

本文大量参考吴永辉教授在2020年度集训讲课内容,可以在网站上进行视频的查找


2021-3-24写了两行


代码里加了注释,但是还不是非常完美,仍要完善~

先存个模板,继续更


2021-3-25第一次更


Tarjan算法是罗伯特 · 塔扬提出的一种求解有向图的强连通分量的一种算法,除了求强连通分支,还可以用来求图的割点和桥,以及点(边)双连通分支,非常的强大,如果想要一睹Tarjan真容,可以移步 bilibili


如果两个点之间可以相互到达,那么说这两个点之间的关系就是强联通的,如果在一个图中,任意两点之间都是可以相互到达的,那么说这个图就是个强连通图。在一个图中,有向图的极大强连通子图叫做这个图的强连通分量SCC

比如说有这么一张图:

微信图片_20220609154733.png

在这张图中,{1,3,2,4},{5},{6}三部分就是强联通的

Tarjan算法是基于DFS的算法,每个强连通分量为当前搜索树中的一棵子树。在算法进行时,将当前的节点1放到里面。这样一来,在回溯时,就可以判断栈顶到栈中的节点是否为一个强连通分量


敲黑板


在Tarjan算法推进的过程中,一些需要定义的数组以及变量,现在首先进行声明一下:

dfn[x],表示节点x的时间戳,通俗说就是当前这个点x被访问的次序,第一个被访问的dfn[x]是1,第二个dfn[x] = 2

low[x],表示节点x或者是x的子树能够追溯到1的最早的栈中节点的编号;low[x]一开始是dfn[x],然后会不断更新,成为强连通分支子树根节点的dfn,当dfn[x] == low[x]的时候,以x为根的搜索子树上所有的节点是一个强连通分支

stack st;实现栈在Tarjan中的作用;

pos[x] or colour[x]可以记录节点x所在的强连通分支是第几个


为了方便,直接手写这个1过程吧


微信图片_20220609154846.jpg


微信图片_20220609154858.jpg


微信图片_20220609154906.jpg


#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 7;
int n,m;
int head[maxn];
int tim,cnt;
struct node {
  int v;
  int nex;
} e[maxn];
void init() {
  cnt = 0;
  tim = 0;
  for(int i=1; i<=n; i++) {
    head[i] = -1;
  }
}
void add(int u,int v) {
  e[cnt].v = v;
  e[cnt].nex = head[u];
  head[u] = cnt ++;
}
stack<int> st; 
//low[x] -> x or x子树能够到达的栈中节点的次序号 
int dfn[maxn],low[maxn],pos[maxn];
int tot;
/// pos[i] == x -> 点i在第x个强连通分支 
void Tarjan(int x) {
  dfn[x] = low[x] = ++tim;///时间戳 
  st.push(x);///节点入栈 
  for(int i=head[x]; ~i; i=e[i].nex) {
    int v = e[i].v;///有向边 
    if(!dfn[v]) {//节点v没有被访问过,是树枝边 
      Tarjan(v);//首次被访问不在栈内 
      low[x] = min(low[x],low[v]);
    } else if(!pos[v]) {///被访问过,在栈内 
    //遇到已经在栈内的点,就将该点作为强连通分量的根 
      low[x] = min(low[x],dfn[v]);
    }
  }//low[x] 没有更新,在栈中节点u以及之上节点构成一个强连通分支 
  if(low[x] == dfn[x]) {
    pos[x] = ++tot;///记录强连通分支的个数 
    while(st.top() != x) {
      pos[st.top()] = tot;//栈中节点u以及之上节点在第tot个强连通分支中 
      st.pop();
    }
    st.pop();//节点u出栈 
  }
}
int main() {
  cin >> n >> m;
  init();
  for(int i=1; i<=m; i++) {
    int a,b;
    cin >> a >> b;
    add(a,b);
  }
  Tarjan(1);///wrong
  for(int i=1;i<=n;i++){///yes
    if(dfn[i] == 0) Tarjan(i);
  }
  cout<<tot<<endl;
  return 0;
}
目录
相关文章
|
9月前
|
存储 算法 数据建模
【最短路算法】SPFA
【最短路算法】SPFA
51 0
|
7月前
|
人工智能
poj 2299 Ultra-QuickSort 求逆序数 树状数组解法
所谓离散化,我们的了解就是对原数组排序,然后用所在位置的下标代替原数,这样我们就可以把数据范围缩小到1-500000,这个数组是开的下的。
23 0
|
人工智能 vr&ar
SPOJ - COT Count on a tree(主席树 LCA)
SPOJ - COT Count on a tree(主席树 LCA)
72 0
|
算法
spfa 算法模板
spfa 算法模板
72 0
[HDU 4738] Caocao‘s Bridges | Tarjan 求割边
Problem Description Caocao was defeated by Zhuge Liang and Zhou Yu in the battle of Chibi. But he wouldn’t give up. Caocao’s army still was not good at water battles, so he came up with another idea. He built many islands in the Changjiang river,
112 0
|
机器学习/深度学习 人工智能 算法
[洛谷] [NOIP2018 提高组] 旅行 加强版 - 基环树 | DFS / Tarjan / topoSort
题目描述 小 Y 是一个爱好旅行的 OIer。她来到 X 国,打算将各个城市都玩一遍。 小Y了解到, X国的 n 个城市之间有 m 条双向道路。每条双向道路连接两个城市。 不存在两条连接同一对城市的道路,也不存在一条连接一个城市和它本身的道路。并且, 从任意一个城市出发,通过这些道路都可以到达任意一个其他城市。小 Y 只能通过这些 道路从一个城市前往另一个城市。
96 0
[洛谷] [NOIP2018 提高组] 旅行 加强版 - 基环树 | DFS / Tarjan / topoSort