暑假刷了一堆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第五天学习笔记

    1.列表补充 extend() li = ["alex", "WuSir", "ritian", "barry", &q ...

  2. 用户交互Scanner

    用户交互Scanner java.util.Scanner Scanner类可以获取用户的输入. Java 5 通过Scanner类的next()和nextLine()方法获取输入的字符串 在读取前我 ...

  3. 代码杂谈-split函数

    java split 函数默认会清除空白行尾的空白. 为了避免这个问题, 需要加参数, 改为 String[] tmpValues = line.split(",", -1);

  4. JavaWeb部分视频\2-12JSP,EL和JSTL

    JavaWeb知识结构图 第3节 EL介绍和运算符 && 第4节 EL获取域中存储的数据 ## EL表达式 1. 概念:Expression Language 表达式语言 2. 作用: ...

  5. UML-为什么要使用层?

    1.内聚职责:使关系分离.减少耦合和依赖,提高潜在复用性. 2.领域层和技术服务层可以是分布式的 3.利于团队开发

  6. python使用进程池多进程时,如何打印错误信息

    一.说明 1.python进程池进行多进程运行时,如果有错误,该进程会直接跳过,并且不会打印错误信息. 2.如果需要了解到进程内的错误信息,此时就需要通过捕获异常来输出错误信息了. 二.具体方法如下: ...

  7. ZJNU 1534 - Problem Robot--高级

    因为是从(0,0)点开始以1,3,9,27,....的步数走的 其实可以每走一步后,以机器人为中心,平面所有坐标全部缩小3倍 那么本应该走3步的路现在只需要走1步就可以到达那个点 那么对于机器人来说这 ...

  8. 【转】我们为什么要使用 Markdown

    目录 从前码字时我们面临着什么困境 标记语言显神威 到底什么是 Markdown 所以为什么我们要使用 Markdown Markdown 简明语法 段落和换行 标题 区块引用 列表 强调 代码标识和 ...

  9. 5.docker image (镜像)

    1.image 是什么 是文件和 meta data 的集合 (root filesystem) 是分层的,并且每一层都可以添加改变删除文件,成为一个新的image 不同的image可以共享相同的la ...

  10. 蓝桥杯 Car的旅行路线 (预处理+最短路径)

    https://www.luogu.org/problem/P1027 题目描述 又到暑假了,住在城市A的Car想和朋友一起去城市B旅游.她知道每个城市都有4个飞机场,分别位于一个矩形的4个顶点上,同 ...