ACM(图论)——tarjan算法详解

---恢复内容开始---

tarjan算法介绍:

  一种由Robert Tarjan提出的求解有向图强连通分量的线性时间的算法。通过变形,其亦可以求解无向图问题

  桥:

  割点:

  连通分量:

  

适用问题:

  求解(有向图/无向图)的,桥,割点,环,回路等问题

整体思想:

  如果我们欲要求解,桥的个数,割点的个数,环的数目,归根结底,是分析清楚一个图 有几个 环,每个环包含哪些节点,那些边。

  而 tarjan算法就是做的这件事情,通过dfs遍历每一条边和节点,算出有几个环,每个环中有哪些节点。那么是如何做的呢,整体上我们可以这么理解。

  首先定义两个 数组 dfn[i]  low[i]  分别代表 每个节点的代号  和 每个环的代号,还有一个分析的次序 cnt变量。然后我们用dfs 深度遍历每一个顶点,每遍历一个节点 cnt++,low[i]=dfn[i]=cnt 就是说,每个节

  点的代号代表它是第几个被我们分析的,同时low[i]=cnt 是说刚开始我们无法判断哪些节点能组成环,所以就初始化为每个节点自己是一个环,这个环的代号就是自己的代号。而当我们分析到一定程度后

  发现,某些节点能组成一个环,再回溯,将这几个节点所属环代号改为一个统一的环代号。到最后,如果一个节点的环代号和自己的代号相等,说明他是一个割点,而如果不相等,则这个节点属于一个

  环,并且通过环代号知道他是那个环。

思想详细分解:

  

  这里我们需要对一些变量重新定义其在代码中的含义,之前所说只是理解上的含义。

  dfn[i]:节点 i dfs的次序

  low[i]: 节点 i 所在环中 所有节点中最小的 dfn[i]。

  vis[i]:这个节点是否被遍历过,遍历过为1,否则为0

  cnt:遍历次数

  a[60]:用来存储图的 vector。

  -------------------------------------------------------------------------------------------------详细的分步分析------------------------------------------------------------------------------------------------------------------------------------------------------

  假如我们从  节点1  开始遍历, low[1]=dfn[1]=cnt=1; 然后遍历他的下一个节点,如果下一个节点曾经遍历过,说明这里能形成一个环,如果没便利过,就接着往下遍历。下一个是 节点2

  low[2]=dfn[2]=cnt=2; 再下一个是 节点3  low[3]=dfn[3]=cnt=3 , 然后  low[4]=dfn[4]=cnt =4,直到 4 的下一个节点可以到 节点2 曾经便利过 ,因此节点2到节点4之间会有一个环,接着我们向后回溯

  使 low[4]=min(low[4],low[2])=2   low[3]=min(low[3],low[4])=2   low[2]=min(low[2],low[3])=2  low[1]=min(low[1],low[2])=1, 我们就会发现 节点2 ,节点3 ,节点4,的low与dfn就不一样了,且low都等于2,说明

  他们三个组成了一个环,然后接着向下遍历 节点5 ,没有被遍历过,所以low[5]=dfn[5]=cnt=5;

  至此 整体上 low[2]=low[3]=low[4]=2     low[1]=dfn[1]=1     low[5]=dfn[5]=5; 所以,这个图中有那几个群 ,群里有哪些节点,有哪些是割点,桥等 都一目了然了。

  下面是详细的代码

  求一个无向图的所有桥的个数,先输入两个数 n个节点,m条边。再输入 m对 数代表边的两个节点。

代码示例:

  

#include <bits/stdc++.h>

using namespace std;
vector<int> a[60];
int dfn[60];
int low[60];int vis[60];
int cnt=1;
int dig=0;//这里是tarjan代码模板核心
void tarjan(int w,int u){
    vis[u]=1;
    low[u]=dfn[u]=(cnt++);
    for(int i=0;i<a[u].size();i++){
        int v = a[u][i];
        if(v==w) continue;//因为是无向图,防止向后返回去遍历
        if(vis[v]!=1) tarjan(u,v);//没有便利过,就接着向下遍历,直到找到后,再执行下面的代码,也就是回溯。
        if(vis[v]==1) low[u]=min(low[u],low[v]);
    }
    if(low[u]==dfn[u]){
        dig++;
    }
}
void init(){  //初始化函数
    memset(dfn,0,sizeof dfn);
    memset(low,0,sizeof low);
    memset(vis,0,sizeof vis);
    memset(sp,0,sizeof sp);
}
int main()
{
    int n,m;
    cin>>n>>m;
    init();
    for(int i=0;i<m;i++){
        int k1,k2;
        cin>>k1>>k2;
        a[k1].push_back(k2);
        a[k2].push_back(k1);
    }
    tarjan(1,1);
    cout<<dig-1<<endl;
    return 0;
}

---恢复内容结束---

原文地址:https://www.cnblogs.com/henulhy/p/10283655.html

时间: 2024-10-09 09:34:13

ACM(图论)——tarjan算法详解的相关文章

Tarjan算法详解

Tarjan算法详解 [概念] 在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected).如果有向图G的每两个顶点都强连通,称G是一个强连通图.非强连通图有向图的极大强连通子图,称为强连通分量(strongly connected components). 下图中,子图{1,2,3,4}为一个强连通分量,因为顶点1,2,3,4两两可达.{5},{6}也分别是两个强连通分量. [功能] Tarjan算法的用途之一是,求一个有向图G=(V,E)里极大强连

Tarjan算法详解理解集合

[功能] Tarjan算法的用途之一是,求一个有向图G=(V,E)里极大强连通分量.强连通分量是指有向图G里顶点间能互相到达的子图.而如果一个强连通分量已经没有被其它强通分量完全包含的话,那么这个强连通分量就是极大强连通分量. [算法思想] 用dfs遍历G中的每个顶点,通dfn[i]表示dfs时达到顶点i的时间,low[i]表示i所能直接或间接达到时间最小的顶点.(实际操作中low[i]不一定最小,但不会影响程序的最终结果) 程序开始时,time初始化为0,在dfs遍历到v时,low[v]=df

Tarjan算法详解(转)

思想: 做一遍DFS,用dfn[i]表示编号为i的节点在DFS过程中的访问序号(也可以叫做开始时间)用low[i]表示i节点DFS过程中i的下方节点所能到达的开始时间最早的节点的开始时间.初始时dfn[i]=low[i] 在DFS过程中会形成一搜索树.在搜索树上越先遍历到的节点,显然dfn的值就越小. DFS过程中,碰到哪个节点,就将哪个节点入栈.栈中节点只有在其所属的强连通分量已经全部求出时,才会出栈. 如果发现某节点u有边连到搜索树中栈里的节点v,则更新u的low 值为dfn[v](更新为l

KM算法详解[转]

KM算法详解 原帖链接:http://www.cnblogs.com/zpfbuaa/p/7218607.html#_label0 阅读目录 二分图博客推荐 匈牙利算法步骤 匈牙利算法博客推荐 KM算法步骤 KM算法标杆(又名顶标)的引入 KM流程详解 KM算法博客推荐 0.二分图 二分图的概念 二分图又称作二部图,是图论中的一种特殊模型. 设G=(V, E)是一个无向图.如果顶点集V可分割为两个互不相交的子集X和Y,并且图中每条边连接的两个顶点一个在X中,另一个在Y中,则称图G为二分图. 可以

并查集算法详解

更好的阅读体验 并查集算法详解 算法详解 维护类型 身为一个数据结构,我们的并查集,它的维护对象是我们的关注点. 并查集适合维护具有非常强烈的传递性质,或者是连通集合性质. 性质详解 传递性质 传递性,也就是具有传递效应的性质,比如说A传递给B一个性质或者条件,让B同样拥有了这个性质或者条件,那么这就是我们所说的传递性. 连通集合性质 连通集合性,和数学概念上的集合定义是差不多的, 比如说A和B同属一个集合,B和C同属一个集合,那么A,B,C都属于同一个集合.这就是我们所谓的连通集合性质. 算法

EM算法(3):EM算法详解

目录 EM算法(1):K-means 算法 EM算法(2):GMM训练算法 EM算法(3):EM算法详解

[转] KMP算法详解

转载自:http://www.matrix67.com/blog/archives/115 KMP算法详解 如果机房马上要关门了,或者你急着要和MM约会,请直接跳到第六个自然段.    我们这里说的KMP不是拿来放电影的(虽然我很喜欢这个软件),而是一种算法.KMP算法是拿来处理字符串匹配的.换句话说,给你两个字符串,你需要回答,B串是否是A串的子串(A串是否包含B串).比如,字符串A="I'm matrix67",字符串B="matrix",我们就说B是A的子串.

[搜索]波特词干(Porter Streamming)提取算法详解(3)

 接上 [搜索]波特词干(Porter Streamming)提取算法详解(2) 下面分为5大步骤来使用前面提到的替换条件来进行词干提取. 左边是规则,右边是提取成功或者失败的例子(用小写字母表示). 步骤1 SSES -> SS                   caresses  ->  caress IES  -> I                          ponies    ->  poni ties      ->  ti SS   -> S

KMP算法详解(图示+代码)

算法过程非常绕,不要企图一次就能看明白,多尝试就会明白一些.下面试图用比较直观的方法解释这个算法,对KMP算法的解释如下: 1. 首先,字符串"BBC ABCDAB ABCDABCDABDE"的第一个字符与搜索词"ABCDABD"的第一个字符,进行比较.因为B与A不匹配,所以搜索词后移一位. 2. 因为B与A不匹配,搜索词再往后移. 3. 就这样,直到字符串有一个字符,与搜索词的第一个字符相同为止. 4. 接着比较字符串和搜索词的下一个字符,还是相同. 5. 直到字