[Tarjan 学习笔记](无向图)
今天考试因为不会敲 Dcc 的板子导致没有AK(还不是你太菜了),所以特地写一篇博客记录 Tarjan 的各种算法
无向图的割点与桥
(各种定义跳过)
割边判定法则
无向边 (x,y) 是桥,当且仅当搜索树上存在 x 的一个子节点 y ,满足:
dfn[x]<low[y]
根据定义, dfn[x]<low[y] 说明从 subtree(y) 出发,在不经过 (x,y) 的前提下,不管走哪条边,都无法到达 x 或比 x 更早访问的节点。若把 (x,y) 删除,则 subtree(y) 就好像一个封闭的环境,与节点 x 之间没有 边相连,图断开成了两部分。因此 (x.y) 是割边。
下面的程序记录求出一张无向图中所有的桥。特别需要注意,通过边 xor 1 来判断是否通过一条无向边访问到了父节点。
- int head[N];
- int tot,sum;
- int n,m,cnt=;
- bool bridge[N];
- int dfn[N],low[N];
- struct Edge{
- int to,nxt;
- }edge[M];
- void tarjan(int now,int in_edge){
- dfn[now]=low[now]=++sum;
- for(int i=head[now];i;i=edge[i].nxt){
- int to=edge[i].to;
- if(!dfn[to]){
- tarjan(to,i);
- low[now]=std::min(low[now],low[to]);
- if(low[to]>dfn[now])
- bridge[i]=bridge[i^]=;
- }
- else if((i^)!=in_edge) low[now]=std::min(low[now],dfn[to]);//记得加括号
- }
- }
割边
割点判定法则
若 x 不是搜索树的根节点,则 x 是割点当且仅当搜索树上存在一个 x 的子节点 y ,满足:
dfn[x]≤low[y]
特别地,若 x 是搜索树的根节点,则 x 是割点当且仅当搜索树上存在至少两个子节点 y1,y2 满足上述条件。
下面的程序求出一张无向图中所有的割点。因为割点判定法则是小于等于号,所以在求割点时,不必考虑父节点和重边的问题,从 x 出发能访问到的所有节点的时间戳都可以用来更新 low[x]。
- int root,n,m;
- bool is_cut[N];
- int stk[N],top;
- int cnt,tot,sum;
- int dfn[N],low[N];
- struct Edge{
- int to,nxt;
- }edge[M];
- void tarjan(int now){
- dfn[now]=low[now]=++sum;
- int flag=;
- for(int i=head[now];i;i=edge[i].nxt){
- int to=edge[i].to;
- if(!dfn[to]){
- tarjan(to);
- low[now]=std::min(low[now],low[to]);
- if(low[to]>=dfn[now]){
- flag++;
- if(now!=root||flag>) cut[now]=;
- }
- }
- else low[now]=std::min(low[now],dfn[to]);
- }
- }
割点
无向图的双联通分量
定理
一张无向连通图是“点双联通图”,当且仅当满足下列两个条件之一:
- 图的顶点数不超过2。
- 图中任意两点都同时包含在至少一个简单环中。其中“简单环”指的是不自交的环,也就是我们通常画出的环。
一张无向连通图是“边双联通图”,当且仅当任意一条边都包含在至少一个简单环中。
边双联通分量(e-DCC)的求法
边双联通分量的计算非常容易。只需求出无向图中所有的桥,把桥都删除后,无向图会分成若干个联通块,每一个联通块就是一个“边双联通分量”。
在具体的程序实现中,一般先用 Tarjan 算法标记出所有的桥边。然后,再对整个无向图执行一次深度优先遍历(遍历的过程不访问桥边),划分出每个联通块。下面代码在 Tarjan 求桥的参考程序基础上,计算出数组 c ,c[x] 表示节点 x 所属的“边双联通分量”的编号。
- int c[N],dcc;
- void dfs(int now){
- c[now]=dcc;
- for(int i=head[now];i;i=edge[i].nxt){
- int to=edge[i].to;
- if(c[to]||bridge[i]) continue;
- dfs(to);
- }
- }
- //以下代码片段加在 main 函数中
- for(int i=;i<=n;i++){
- if(!c[i]) ++dcc,dfs(i);
- }
e-DCC
e-DCC的缩点
把每个 e-DCC 看做一个节点,把桥边 (x,y) 看作连接编号为 c[x] 和 c[y] 的无向边,会产生一棵树(若原图不连通,则产生森林)。这种把 e-DCC 收缩为一个节点的方法就称为“缩点”。下面的代码在 Tarjan 求桥、求 e-DCC 的参考程序基础上,把 e-DCC 缩点,构成一棵新的树(或森林),存储在另一个邻接表中。
- int rcnt=;
- int rhead[N];
- struct Redge{
- int to,nxt;
- }redge[M];
- void add_c(int x,int y){
- redge[++rcnt].to=y;
- redge[rcnt].nxt=rhead[x];
- rhead[x]=rcnt;
- }
- //以下代码片段加在 main 函数中
- for(int i=;i<=cnt;i++){
- int x=edge[i].to;
- int y=edge[i^].to;
- if(c[x]==c[y]) continue;
- add_c(c[x],c[y]);
- add_c(c[y],c[x]);
- }
e-DCC缩点
点双联通分量(v-DCC)的求法
若某个节点为孤立点,则它自己单独构成一个 v-DCC。除了孤立点之外,点双联通分量的大小至少是 2。根据 v-DCC 定义中的“极大”性,虽然桥不属于任何 e-DCC,但是割点有可能属于多个 v-DCC。
为了求出“点双联通分量”,需要在 Tarjan 算法的过程中维护一个栈,并按照如下方法维护栈中的元素:
1.当一个节点第一次被访问时,把该节点入栈。
2.当个点判定法则中的条件 dfn[x]≤low[y] 成立时,无论 x 是否为根,都要:
(1) 从栈顶不断弹出节点,直至节点 y 被弹出。
(2) 刚才弹出的所有节点与节点 x 一起构成一个 v-DCC。
下面的程序在求出割点的同时,计算出 vector 数组 dcc,dcc[i] 保存编号为 i 的 v-DCC 中的所有节点。
- void tarjan(int now){
- stk[++top]=now;
- dfn[now]=low[now]=++tot;
- for(int i=head[now];i;i=edge[i].nxt){
- int to=edge[i].to;
- if(!dfn[to]) {
- tarjan(to),low[now]=min(low[now],low[to]);
- if(low[to]>=dfn[now]) {
- sum++;
- do{
- z=stk[top--];
- }while(z!=to);
- }
- } else low[now]=min(low[now],dfn[to]);
- }
- }
[Tarjan 学习笔记](无向图)的更多相关文章
- Tarjan学习笔记
\(Tarjan\)是个很神奇的算法. 给一张有向图,将其分解成强连通分量们. 强连通分量的定义:一个点集,使得里面的点两两可以互相到达,并且再加上另一个点都无法满足强连通性. \(Tarjan\)的 ...
- $tarjan$简要学习笔记
$QwQ$因为$gql$的$tarjan$一直很差所以一直想着要写个学习笔记,,,咕了$inf$天之后终于还是写了嘻嘻. 首先说下几个重要数组的基本定义. $dfn$太简单了不说$QwQ$ 但是因为有 ...
- 仙人掌&圆方树学习笔记
仙人掌&圆方树学习笔记 1.仙人掌 圆方树用来干啥? --处理仙人掌的问题. 仙人掌是啥? (图片来自于\(BZOJ1023\)) --也就是任意一条边只会出现在一个环里面. 当然,如果你的图 ...
- 概率图模型学习笔记:HMM、MEMM、CRF
作者:Scofield链接:https://www.zhihu.com/question/35866596/answer/236886066来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商 ...
- Day 4 学习笔记 各种图论
Day 4 学习笔记 各种图论 图是什么???? 不是我上传的图床上的那些垃圾解释... 一.图: 1.定义 由顶点和边组成的集合叫做图. 2.分类: 边如果是有向边,就是有向图:否则,就是无向图. ...
- OI数学 简单学习笔记
基本上只是整理了一下框架,具体的学习给出了个人认为比较好的博客的链接. PART1 数论部分 最大公约数 对于正整数x,y,最大的能同时整除它们的数称为最大公约数 常用的:\(lcm(x,y)=xy\ ...
- OI知识点|NOIP考点|省选考点|教程与学习笔记合集
点亮技能树行动-- 本篇blog按照分类将网上写的OI知识点归纳了一下,然后会附上蒟蒻我的学习笔记或者是我认为写的不错的专题博客qwqwqwq(好吧,其实已经咕咕咕了...) 基础算法 贪心 枚举 分 ...
- 【学习笔记】Kruskal 重构树
1. 例题引入:BZOJ3551 用一道例题引入:BZOJ3551 题目大意:有 \(N\) 座山峰,每座山峰有他的高度 \(h_i\).有些山峰之间有双向道路相连,共 \(M\) 条路径,每条路径有 ...
- TensorFlow学习笔记5-概率与信息论
TensorFlow学习笔记5-概率与信息论 本笔记内容为"概率与信息论的基础知识".内容主要参考<Deep Learning>中文版. \(X\)表示训练集的设计矩阵 ...
随机推荐
- tcp/ip 卷一 读书笔记(3)为什么既要有IP地址又要有MAC地址
网络层 首先明确一点,并不是所有的网络之间传输数据都需要mac地址和ip地址,比如说点对点线路之间的通信就没有MAC地址,网络层使用ipx协议时就没有ip地址,但是在当前的主流网络中,我们都使用ip地 ...
- Spark SQL1.2测试
Spark SQL 1.2 运行原理 case class方式 json文件方式 背景:了解到HDP也能够支持Spark SQL,但官方文档是版本1.2,希望支持传统数据库.hadoop平台.文本格式 ...
- HTML入门标签汇总
HTML入门标签汇总 1.<div></div>用于定义文档的区块,用来划分出独立不同的部分. 2.<h1></h1>数字1-6定义从大到小的标题. 3 ...
- linux memcached Session共享
memcached memcached是高性能的分布式缓存服务器用来集中缓存数据库查询结果,减少数据库访问次数提高动态web应用的响应速度 传统web架构的问题许多web应用都将数据保存在RDBMS中 ...
- jsencrypt参数前端加密c#解密
写程序时一般是通过form表单或者ajax方式将参数提交到服务器进行验证,如何防止提交的请求不被抓包后串改,虽然无法说绝对安全却给非法提交提高了难度,本篇采用jsencypt在前端进行加密的并且用C# ...
- FusionCharts 3D帕累托图
1.设计3D帕累托图的页面 Pareto3D.html: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN&q ...
- Python Numpy包安装
1,下载python 下载地址: https://www.python.org/downloads/windows/ 2,配置python环境变量 在电脑的系统属性的系统变量path中添加python ...
- PL/SQL集合 ----- varrays
varrays可以再表,记录,对象定义中使用,类似于C中的数组. 1.定义varrays用作PL/SQL程序构造块. declare type integer_varray ) of integer; ...
- 错误代码: 1248 Every derived table must have its own alias
1.错误描述 1 queries executed, 0 success, 1 errors, 0 warnings 查询:SELECT stu_id, (SELECT stu_name FROM t ...
- freemarker写select组件报错总结(五)
1.错误描述 六月 26, 2014 10:44:49 下午 freemarker.log.JDK14LoggerFactory$JDK14Logger error 严重: Template proc ...