Tarjan算法

概念区分

  • 有向图

    • 强连通:在有向图\(G\)中,如果两个顶点\(u, v\ (u \neq v)\)间有一条从\(u\)到\(v\)的有向路径,同时还有一条从\(v\)到\(u\)的有向路径,则称\(u, v\)强连通
    • 强连通图:如果有向图\(G\)的任意两个不同的顶点都强连通,则称\(G\)是一个强连通图
    • 强连通分量:有向图\(G\)的极大强连通子图称为图\(G\)的强连通分量
  • 无向图
    • 连通:和强连通类似(只是无向图的任意边都是双向的,如果存在\(u\rightarrow v\)的路径,必然存在\(v\rightarrow u\)的路径)
    • 连通图:如果无向图\(G\)的任意两不同的顶点都连通,则称\(G\)是一个连通图
    • 连通分量:无向图\(G\)的极大连通子图称为图\(G\)的连通分量
    • 割点(割顶):在无向图\(G\)中,如果删除一个点\(u\)及以\(u\)为端点的所有边后,图的连通分量个数增多,则称点\(u\)为割点(割顶)
    • 桥:在无向图\(G\)中,如果删除一条边$e=(u,v)\ \((连接\)u,v\(两点的边)后,图的连通分量个数增多,则称边\)e$为,也称割边
    • 双连通:
      • 双连通图:分为点双连通图边双连通图,若一个无向图\(G\)中的去掉任意一个节点(一条边)都不会改变图\(G\)的连通性,即不存在割点(桥),则称图\(G\)点(边)双连通图
      • 边双连通图:一个无向图\(G\)中的每一个极大点双连通子图边双连通子图分别称为无向图\(G\)的点双连通分量(BCC)边双连通分量(e-BCC)

Tarjan

DFS​树

\(tarjan\)的过程实际上就是一个\(dfs\)的过程,对图\(G\)进行\(dfs\)会得到一棵\(dfs\)树,在有向图的\(dfs\)树中有四种边,树边、回边(返祖边、后向边)、横叉边和前向边。树边顾名思义,回边是指在\(dfs\)过程中搜索到已经访问过的点,但它的子树未访问完成;横叉边是指搜索到已经访问过的点且它的子树也已访问完成,而且横叉边连接的两个点不是祖先和后代的关系;前向边与横叉边唯一的不同在于前向边连接的两个点祖先和后代的关系,读者可以结合下图理解这四种边

⏫图片来源:https://www.cnblogs.com/gongpixin/p/5003049.html

强连通分量

思想

  • \(dfn[u]\):顶点\(u\)被访问到的时间戳,每个点的\(dfn\)被赋值后就不会改变

  • \(low[u]\):顶点\(u\)能够到达的点中的\(dfn\)最小值

  • \(stack[top]\):可能构成强连通分量的点的集合

  • \(vis[u]:\)顶点\(u\)是否在栈中

  • \(color[u]\):顶点\(u\)的颜色,用于区分不同的强连通分量

    // 有些题目添加\(color\)数组比较方便,不是必要的

采用深度优先搜索的思想,对每一个可能的强连通分量进行\(dfs\):维护一个可能构成强连通分量的集合\(stack\),将搜索到的点加入\(stack\),并维护其\(dfn,\ vis\)值,往下搜它的边连接的顶点,在回溯的时候维护\(low\)的值,如果点\(u\)的\(dfn==low\)说明它是某个强连通分量子树的根,此时我们找到了一个强连通分量,只要将栈\(stack\)中的点弹出,直到弹出\(u\)

  • 同一个强连通分量中的点的\(low\)值是相同的

看到这儿,有些读者可能会问,那无向图的连通分量咋求?也是直接套\(tarjan\)的板子吗?如果您有这样困惑反正我是有过,那是学算法学傻了不是,无向图还\(tarjan\)啥,直接\(dfs\)或\(bfs\)一遍就完了,那善于思考的读者们又会问了,为啥无向图这么方便?因为对于无向图,如果存在路径\(u\rightarrow v\),那路径\(v\rightarrow u\)必然存在,\(u\)能跑到的点(包括\(u\)自己)都是一个连通分量里的

板子

有向图强连通分量

void tarjan(int u) {
dfn[u] = low[u] = ++tim;
vis[u] = true; // 入栈
stack[++top] = u;
int size = g[u].size();
for(int i = 0; i < size; ++i) {
int v = g[u][i];
if(!dfn[v]) { // 树边,继续下搜
tarjan(v);
low[u] = min(low[u], low[v]);
} else if(vis[v]) // 回边,更新low[u]
low[u] = min(low[u], dfn[v]);
}
if(dfn[u] == low[u]) {
color[u] = ++sum;
vis[u] = false;
while(stack[top] != u) {
color[stack[top]] = sum;
vis[stack[top--]] = false;
}
top--;
}
} //for(int i = 1; i <= n; ++i)
// if(!dfn[i])
// tarjan(i);

割点(顶) / 桥

原理

在\(tarjan\)算法求割点的过程中,主要涉及到两种边:树边和回边(返祖边),意义已在上文中提到⏫

  • 定理\(1\):在无向图\(G\)中,点\(u(u不是根节点)\)是割点 \(\iff\) \(u\)的某个子树\(T\)中不含有返回\(u\)的祖先(不包括\(u\))的边 \(\iff\) \(low[v] >= dfn[u]\)

    特别地,根节点是割顶 \(\iff\) 它的子节点数目大于\(1\)

    画图YY一下:

    上图的\(u\)的某个子树\(T\)中虽然有返回\(u\)的回边,但是没有返回\(u\)的祖先(不包含\(u\))的回边,所以\(u\)是割点,但是边\(e\)并不是桥

  • 定理\(2\):在无向图\(G\)中,边\((u,v)\)是桥 \(\iff\) \(u\)的某个子树\(T\)中不含有返回\(u\)的祖先(包括\(u\))的边 \(\iff\) \(low[v] > dfn[u]\)

    还是看之前的图,\(fa\)的某个子树中不含有返回\(fa\)的祖先(包括\(fa\))的回边,所以\(ef\)是桥

  • 可以看出如果一个图\(G\)有桥,它必有割点,因为桥连接的两端必然至少有一个是割点,但反过来不成立

板子

还有点细节:

\(child\)是点\(fa\)的子节点个数,注意\(fa\)的孙子不计算在内

特别的,像:,点\(u\)不是割点,因为去掉它后,这个图的强连通分量的个数并没有增加

void Tarjan(int u, int fa) { // 割点
low[u] = dfn[u] = ++tim;
int sz = g[u].size(), child = 0;
for(int i = 0; i < sz; i++) {
int v = g[u][i];
if(!dfn[v]) {
Tarjan(v, fa);
low[u] = min(low[v], low[u]);
if(low[v] >= dfn[u] && u != fa) iscut[u] = true;
// if(low[v] > dfn[u]) u - v 是桥
if(u == fa) child++;
} else
low[u] = min(low[u], dfn[v]);
}
if(fa == u && child >= 2) iscut[u] = true;
} //for(int i = 1; i <= n; ++i)
// if(!dfn[i])
// tarjan(i, i);

缩点

啥是缩点

就把一个强连通分量(或边双连通分量等,视题目而定)缩成一个点

再看张图YY一下:

光这么讲,大家肯定还是云里雾里,这缩点到底能干啥?下面我们通过两道例题来看看缩点到底能干啥

例题

  • 洛谷P3387 缩点

    思路:缩点,把一个强连通分量缩成一个点,新点的权值等于强连通分量里的所有点的点权之和,缩完点就能得到一个有向无环图\((DAG)\),然后就可以在这个\(DAG\)上跑\(dp\)了,然后就能得到答案

双连通分量

一点性质

  • 边双连通分量(e-BCC)满足任意两点间都有至少两条边不重复的路径
  • 点双连通分量(BCC)满足任意两点间都有至少两条点不重复路径
  • 有桥必有割点,一个点双连通分量必然是边双连通分量,反之,不一定成立

边双连通分量(e-BCC)

如上图,左右两个用大圈圈出来的分别是两个边双连通分量

求法:

把桥全部去掉剩下的独立的分量都是边双连通分量,所以只要一遍\(tarjan\)找到桥,再一遍\(dfs\)就能能求出边双连通分量

点双连通分量(BCC)

如上图,左边红圈圈出的和紫色圈圈出的分别是两个点双连通分量,红点是整张图的一个割点

两个点双连通分量间至多有一个公共点,且它一定是割点;不是割点的点,一定属于某个点双连通分量

求法:

只要分离了割点,就可以把点双连通分量分离出来,但是一个割点可能同属于两个点双连通分量,所以一般用分量中的边来输出,可以用栈保存遍历到的边,一旦发现割顶就弹出属于一个BCC的边

例题

  • POJ3352 Road Construction

    思路:题意是问最少加几条边可以使整个图变成边双连通的,即问最少加多少条边可以使整张图的任意两点间至少有两条边不重复路径,所以我们可以将已有的边双连通分量缩点,缩完点形成一棵树。现在,题目变为在缩完点后的树上最少添加几条边能使其变为边双连通图,有一个结论是对于一棵无向树,我们要使得其变成边双连通图,需要添加的边数= (树上度数为1的点的个数+1)/2

Tarjan算法——强连通、双连通、割点、桥的更多相关文章

  1. 算法笔记_150:图论之双连通及桥的应用(Java)

    目录 1 问题描述 2 解决方案   1 问题描述 Description In order to get from one of the F (1 <= F <= 5,000) graz ...

  2. Tarjan算法初探(3):求割点与桥以及双连通分量

    接上一节Tarjan算法初探(2):缩点 在此首先提出几个概念: 割点集合:一个无向连通图G 若删除它的一个点集 以及点集中所有点相连的边(任意一端在点集中)后 G中有点之间不再连通则称这个点集是它的 ...

  3. tarjan算法--求解无向图的割点和桥

    1.桥:是存在于无向图中的这样的一条边,如果去掉这一条边,那么整张无向图会分为两部分,这样的一条边称为桥 也就是说 无向连通图中,如果删除某边后,图变成不连通,则称该边为桥 2.割点:无向连通图中,如 ...

  4. tarjan算法--求无向图的割点和桥

    一.基本概念 1.桥:是存在于无向图中的这样的一条边,如果去掉这一条边,那么整张无向图会分为两部分,这样的一条边称为桥无向连通图中,如果删除某边后,图变成不连通,则称该边为桥. 2.割点:无向连通图中 ...

  5. Tarjan求强连通分量、求桥和割点模板

    Tarjan 求强连通分量模板.参考博客 #include<stdio.h> #include<stack> #include<algorithm> using n ...

  6. 图的强连通&双连通

    http://www.cnblogs.com/wenruo/p/4989425.html 强连通 强连通是指一个有向图中任意两点v1.v2间存在v1到v2的路径及v2到v1的路径. dfs遍历一个图, ...

  7. tarjan算法强连通分量的正确性解释+错误更新方法的解释!!!+hdu1269

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1269 以下内容为原创,转载请声明. 强连通分量SCC(Strongly Connected Compo ...

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

    tarjan的过程就是dfs过程. 图一般能画成树,树的边有三种类型,树枝边 + 横叉边(两点没有父子关系) + 后向边(两点之间有父子关系): 可以看到只有后向边能构成环,即只有第三张图是强连通分量 ...

  9. HDU4612(Warm up)2013多校2-图的边双连通问题(Tarjan算法+树形DP)

    /** 题目大意: 给你一个无向连通图,问加上一条边后得到的图的最少的割边数; 算法思想: 图的边双连通Tarjan算法+树形DP; 即通过Tarjan算法对边双连通缩图,构成一棵树,然后用树形DP求 ...

随机推荐

  1. 使用Theia——创建扩展包

    上一篇:使用Theia——构建你自己的IDE 创建Theia扩展包 本例中,我们将添加一个菜单项“Say hello”用来显示一个通知“Hello world!”.本文将指导你完成所有必要的步骤. T ...

  2. Java异常处理原则与技巧总结

    一  处理原则 Java异常代码中我们使用异常的目的是让异常的异常类型来提示“什么”被抛出了--- 即出了什么问题:用异常的栈打印信息来跟踪异常在“哪里”抛出 --- 即哪里出了问题: 异常提示信息来 ...

  3. AnyDesk免费远程工具

    AnyDesk是一款声称速度最快的免费长途衔接/长途桌面操控软件,是前TeamViewer开发小组人员自立门户的商品,它拥有领先的视频压缩技能DeskRT,能够轻松穿透防火墙/路由器,实测在电信.移动 ...

  4. 【转】python中查询某个函数的使用方法

    使用help(),例查询sum函数的用法 使用官方文档: 1)打开python的IDLE: 2)点击help,选择python doc(这是python的官方文档,或者你也可以直接按f1键) 3)在调 ...

  5. C# 将Word转为PDF、XPS、Epub、RTF(基于Spire.Cloud.Word.SDK)

    本文介绍通过调用Spire.Cloud.Word.SDK提供的ConvertApi接口将Word转换为PDF.XPS.Epub.RTF以及将Docx转为Doc格式等.调用接口方法及步骤参考以下步骤: ...

  6. 做前端的你还没用这些软件?? out 啦

    1. 编辑器 写代码只是生产软件过程中的一环.无论是数据结构.编译原理.操作系统还是组成原理都是编码的重要基础,试问没有学过编译原理的人能够针对性地进行编译优化吗?不懂操作系统的人能玩得转linux吗 ...

  7. SpringCloud之Eureka(注册中心集群篇)(三)

    一:集群环境搭建 第一步:我们新建两个注册中心工程一个叫eureka_register_service_master.另外一个叫eureka_register_service_backup eurek ...

  8. java 储存机制

    1.栈 statck 局部变量名称 2.堆 heap 带new的 3.方法区 method area .class

  9. Flask DBUtils

    作用:创建连接池,解决多线程问题 1.安装模块 pip3 install -i https://pypi.douban.com/simple DBUtils 2.settings.py(配置文件) f ...

  10. Java单体应用 - 开发工具 - 02.Maven

    原文地址:http://www.work100.net/training/monolithic-tools-maven.html 更多教程:光束云 - 免费课程 Maven 序号 文内章节 视频 1 ...