暑假刷了一堆Tarjan题到头来还是忘得差不多。

这篇博客权当复习吧。

一些定义

无向图

割顶与桥 (划重点)

图G是连通图,删除一个点表示删除此点以及所有与其相连的边。

若删除某点u后G不再连通,那么u是G的一个割顶(割点)。

若删除某边e后G不再连通,那么e是G的一个

双连通

一个图为双连通,意思是说任一点对(u,v),从u到v都有两条路径。

广义双连通有两种:点双连通(狭义的双连通)、边双连通。

  • 点双连通:就是这两条路径除了起点和终点外无重复点。
  • 边双连通:就是这两条路径无重复边。

例如,两个简单环共一个点的图是边双连通的,但不是点双连通的。

双连通图的性质

点双连通图删除任意一个点仍然连通。

边双连通图删除任意一条边仍然连通。

双连通分量

双连通分量也分为两种。

图G的双连通分量也是G的子图,且保证添加任意其他点、边后都不再是双连通的。

一个顶点可以属于多个点双连通分量,此时这个顶点必定是割顶。

显然,点双连通分量必定是边双连通分量。

哈密顿回路和欧拉回路

哈密顿路是经过所有顶点一次的路径。若起点和终点相同则称哈密顿回路。

欧拉路是经过所有边一次的路径。

有向图

有向图的连通性

  • 强连通:任意点对(u,v),存在从u到v的路径从v到u的路径。
  • 单连通:任意点对(u,v),存在从u到v的路径从v到u的路径。亦称单向连通。
  • 弱联通:忽略边的方向得到的无向图是连通的。此无向图称为该有向图的底图。

单连通图一定是弱连通图。反之则不然。

强连通分量(SCC)(划重点)

亦如上面的定义。有向图G的一个强连通分量是G的一个子图,且任意添加其他点、边都不能再满足强连通的要求。

Tarjan算法

Tarjan求割顶

首先从根节点开始dfs遍历全图,对于每个点我们定义两个数组:\(dfn[],low[]\),其中\(dfn[]\)是这个点的时间戳,用来记录首次访问这个点的时刻;而\(low[]\)则是用来记录从这个点出发所能到达点的最小时间戳。可以配合注释食用。

void tarjan(int u,int fath){
dfn[u]=low[u]=++tmp;
int child=0;
for (int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if (!dfn[v]){//如果在此之前点v没有被访问过
tarjan(v,u);
low[u]=Min(low[u],low[v]);//更新最小时间戳
child++;
if (!fath&&child>=2) rec[v]=1;//如果u为根节点且u下有两棵子树那么u一定是割顶
else (fath&&(low[v]>=dfn[u])) rec[v]=1;//如果u有子树的最小时间戳不小于u的时间戳,那么u一定是割顶
}else{
if (u!=fath) low[u]=Min(low[u],dfn[v]);//v的时间戳可能比u小,并且回边不能算
}
}
}

Tarjan求桥

和求割顶非常类似

void tarjan(int u,int fath){
dfn[u]=low[u]=++tmp;
int child=0;
for (int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if (!dfn[v]){
tarjan(v,u);
low[u]=Min(low[u],low[v]);
child++;
if (dfn[u]<low[v]){e[++cnt].u=u;e[cnt].v=v;}
}else {if (u!=fath) low[u]=Min(low[u],dfn[v]);}
}
}

Tarjan求强连通分量

在Tarjan求强连通分量的过程中,low的定义有所改变,并且引入一个栈,low不是能到达的最前的dfn值,而是能到达的仍在栈中的dfn的最前值。

void tarjan(int u){
dfn[u]=low[u]=++timer;
st.push(u); instack[u]=true;
for (int i=0;i<e[u].size();i++) {
int v=e[u][i];
if (dfn[v]==0) {//如果访问过v
tarjan(v);
low[u]=min(low[u],low[v]);//更新最小时间戳
} else if (instack[v]) {//如果在栈内(在同一个强连通分量内)
low[u]=min(low[u],dfn[v]);
/*同low[u]=min(low[u],low[v]);
LXQ:求scc应该都可以-.-因为你一旦找到了上一个scc那它们就都出栈了-.-所以后面更新判断是否在栈中就保证了更新的值都是可行的
求scc不存在割顶的问题所以应该都可以
Upd.其实就是说已经走过的v在栈内,但是由于递归没有回溯,所以此时dfn[v]==low[v]*/
}
}
if (dfn[u]==low[u]) {//这就是一个强连通分量的根节点
int m;
printf("SCC: ");
do {
m=st.top();
st.pop();/*使栈顶出栈,把该强连通分量内除根节点外的
所有点弹出*/
instack[m]=false;
printf("%d ",m);
} while (m!=u);
printf("\n");
}
}

缩点

一般,缩点是针对有向图而言的。

找出了一个有向图的强连通分量后我们可以把每个强连通分量缩为一个点。

如此一来我们便得到一个有向无环图(DAG)。

有了有向无环图,我们就可以在上面跑拓扑。能跑拓朴就能跑dp,因为状态转移的无后效性已经在DAG建立的同时保证了。

例题:

  1. Luogu_3627_[APIO2009]抢掠计划

    一句话题解:对于题目中的起点S的处理,在跑Tarjan的时候记录访问过的点,缩点的时候把没访问过的点排除即可。

练习:

Tarjan缩点+DAG+dp模板题:

Luogu_3387_【模板】缩点

Luogu_4742_[Wind Festival]Running In The Sky

图论初步-Tarjan算法及其应用的更多相关文章

  1. 图论1 Tarjan算法

    强连通分量 模板(强联通分量个数+缩点) #include<iostream> #include<cstdio> #define MAXn 100000 #define MAX ...

  2. 图论:Tarjan算法

    在有向图中,若两点至少包含一条路径可以到达,则称两个顶点强连通,若任意两个顶点皆如此,则称此图为强联通图.非强连通图有向图的极大强连通子图,称为强连通分量(strongly connected com ...

  3. 『图论』有向图强连通分量的Tarjan算法

    在图论中,一个有向图被成为是强连通的(strongly connected)当且仅当每一对不相同结点u和v间既存在从u到v的路径也存在从v到u的路径.有向图的极大强连通子图(这里指点数极大)被称为强连 ...

  4. 【原创】tarjan算法初步(强连通子图缩点)

    [原创]tarjan算法初步(强连通子图缩点) tarjan算法的思路不是一般的绕!!(不过既然是求强连通子图这样的回路也就可以稍微原谅了..) 但是研究tarjan之前总得知道强连通分量是什么吧.. ...

  5. 【算法•日更•第二十八期】图论:强连通+Tarjan算法(一)

    ▎前言 一直都想学习这个东西,以为很难,结果发现也不过如此. 只要会些图论的基础就可以了. ▎强连通 ☞『定义』 既然叫强连通,那么一定具有很强的连通性. 强连通:就是指在一个有向图中,两个顶点可以互 ...

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

    ---恢复内容开始--- tarjan算法介绍: 一种由Robert Tarjan提出的求解有向图强连通分量的线性时间的算法.通过变形,其亦可以求解无向图问题 桥: 割点: 连通分量: 适用问题: 求 ...

  7. 图论分支-Tarjan初步-割点和割边

    所谓割点(顶)割边,我们引进一个概念 割点:删掉它之后(删掉所有跟它相连的边),图必然会分裂成两个或两个以上的子图. 割边(桥):删掉一条边后,图必然会分裂成两个或两个以上的子图,又称桥. 这样大家就 ...

  8. 图论-强连通分量-Tarjan算法

    有关概念: 如果图中两个结点可以相互通达,则称两个结点强连通. 如果有向图G的每两个结点都强连通,称G是一个强连通图. 有向图的极大强连通子图(没有被其他强连通子图包含),称为强连通分量.(这个定义在 ...

  9. Light OJ - 1026 - Critical Links(图论-Tarjan算法求无向图的桥数) - 带详细注释

     原题链接   无向连通图中,如果删除某边后,图变成不连通,则称该边为桥. 也可以先用Tajan()进行dfs算出所有点 的low和dfn值,并记录dfs过程中每个 点的父节点:然后再把所有点遍历一遍 ...

随机推荐

  1. Python笔记_第四篇_高阶编程_正则表达式_2.正则表达式入门

    1. 匹配单个字符和数字: . --->> 匹配除换行符以外的任意字符.[0123456789] --->> []字符集合,表示匹配方括号中所包含的任意一个字符.[Thomas ...

  2. C语言如何获得精确到毫秒的时间

    在做测试或性能优化时,经常要知道程序运行的时间,在Linux系统可以使用time命令来计算程序运行运行所消耗的时间,能精确到毫秒,如果要精确到代码块或某个操作运行时所消耗的时间,time命令就不给力了 ...

  3. python编程:从入门到实践----第五章>if 语句

    一.一个简单示例 假设有一个汽车列表,并想将其每辆汽车的名称打印出来.遇到汽车名‘bmw’,以全大写打印:其他汽车名,首字母大写 cars=['audi','bmw','subaru','toyota ...

  4. SaltStack中状态间关系unless、onlyif、require、require_in、watch、watch_in

    1.unless 检查的命令,仅当unless选项指向的命令返回值为false时才执行name定义的命令 cmd.run: {% "] %} - name: 'nohup sh /alida ...

  5. 一文详解 Java 的八大基本类型

    自从Java发布以来,基本数据类型就是Java语言中重要的一部分,本文就来详细介绍下每种基本类型的具体使用方法和限制. 作者 | Jeremy Grifski 译者 | 弯月,责编 | 郭芮 出品 | ...

  6. Python笔记_第三篇_面向对象_8.对象属性和类属性及其动态添加属性和方法

    1. 对象属性和类属性. 我们之前接触到,在类中,我们一般都是通过构造函数的方式去写一些类的相关属性.在第一次介绍类的时候我们把一些属性写到构造函数外面并没有用到构造函数,其实当时在写的时候,就是在给 ...

  7. 【YOLO学习】召回率(Recall),精确率(Precision),平均正确率(Average_precision(AP) ),交除并(Intersection-over-Union(IoU))

    摘要 在训练YOLO v2的过程中,系统会显示出一些评价训练效果的值,如Recall,IoU等等.为了怕以后忘了,现在把自己对这几种度量方式的理解记录一下. 这一文章首先假设一个测试集,然后围绕这一测 ...

  8. spark安装和使用

    local模式 概述 local模式就是在一台计算机上运行spark程序,通常用于在本机上练手和测试,它将线程映射为worker. 1)local: 所有计算都运行在一个线程当中,没有任何并行计算,通 ...

  9. Adobe Photoshop、Adobe Illustrator、Bohemian的Sketch、Figma比较

    整体来说: Adobe Photoshop:位图图像编辑处理,更适合图片编辑和数码绘画处理: Adobe Illustrator:矢量设计,更适合图标设计和图形设计处理: Sketch:矢量设计,更适 ...

  10. sql表变量,临时表

    @test是表变量,存在于内存中:#是临时表,存在于tempdb数据库空间.