首页 > 其他分享 >Tarjan学习笔记

Tarjan学习笔记

时间:2024-11-25 18:43:53浏览次数:9  
标签:Tarjan int 连通 笔记 stk 学习 dfn low col

强连通分量,缩点算法:Tarjan

代码及模板
强连通图:有向图,任意两点有路径
强连通分量:有向图,强连通子图数量
前置知识:dfs树(dfs序构成的树)
成分:
1.树边:dfs树上的边
(以下三种边是dfs树上没有但原图上有的边)
2.前向边:dfs树的祖先到儿子的边。
3.返祖边(后向边):儿子到祖先的边
4.横向边:旁系亲戚的边(没有直接的祖父关系,可能是兄弟节点之类的)
性质:返祖边和横向边的dfn(dfs序)u < v (不然被直接更新成树边)且因为是dfs,所以v已经判断完了。
所以可以每次遇到一条dfn[u] < dfn[v] 的边,都可以更新low[u] = min(low[u], low[v])
每次递归搜索的时候,都要更新low[u] = min(low[u], low[v])子节点有可能找到了返祖边,那么父节点的low也是儿子的low
循环结束后,如果low[u] = dfn[u],说明u是环的根,栈后面的元素都属于该强联通图,染色,出栈。

具体的代码实现有点有趣。

#include <stack>
 
int dfn[MAXN], tot = 0;
bool instack[MAXN];
int low[MAXN];
int co[MAXN], col = 0;
std::stack<int> stk;
 
void Tarjan(int u)
{
    dfn[u] = ++tot;
    low[u] = dfn[u]; // 一开始low[u]是自己,有后向边再更新
    stk.push(u);
    instack[u] = true;
    for(int e = first[u]; e; e = nxt[e])
    {
        int v = go[e];
        if(!dfn[v])
        {
            Tarjan(v);
            low[u] = min(low[u], low[v]); // 子节点更新了,我也要更新
            // 若子节点没更新,则min能够保证low[u] == dfn[u]
        }
        else if(instack[v]) // v访问过且在栈中,意味着u→v是后向边
        {
            low[u] = min(low[u], dfn[v]);
        }
    }
    if(low[u] == dfn[u]) // 是SCC中的第一个被访问的节点
    {
        co[u] = ++col;
        while(stk.top() != u) co[stk.top()] = col, instack[stk.top()] = false, stk.pop();
            // 染色,弹栈
        instack[u] = false;
        stk.pop(); // 最后把u弹出去
    }
}

缩点

即把同一强连通分量的点变成一个点,新点权为scc里所有点权之和

割点

定义:删了该点图不连通的点叫割点
换句话说:若x是割点,则存在 y∈T(x) 满足y不经过 x 能到达的所有点均属于 T(x)。(注:T(x)表示x的dfs子树)
所有的儿子节点的low小于\(dfn_u\)
使得$ low_v \geq dfn_u $,即不能回到祖先,那么 u 点为割点。

割边(桥)

当枚举到边u-v时,若low_v > low_u时,该边为桥。

边,点双连通分量

定义:
在一张连通的无向图中,对于两个点 u 和 v,如果无论删去哪条边(只能删去一条)都不能使它们不连通,我们就说 u 和 v 边双连通

在一张连通的无向图中,对于两个点 u 和 v,如果无论删去哪个点(只能删去一个,且不能删 u 和 v 自己)都不能使它们不连通,我们就说 u 和 v 点双连通

双连通具有传递性, 而双连通不具有传递性

一个边/点双连通分量 == 边/点双连通子图

边双连通分量求法:
在 DFS 生成树上的一个强连通分量,在原无向图中是边双连通分量。可以发现,求边双连通分量的过程实际上就是求强连通分量的过程。

最后记得判重边****

桥(割边)的求法


如上图所示,黑色与绿色边为树边,红色边为非树边。每一条非树边的两个端点都唯一对应了树上的一条由树边构成的简单路径,我们说这条非树边 覆盖 了这条简单路径上所有的边。

在图中,绿色的树边 至少 被一条非树边覆盖,黑色的树边不被 任何 非树边覆盖。

显然,非树边 和 绿色的树边 一定不是桥,黑色的树边 一定是桥。

首先考虑一个暴力的做法,对于每一条非树边,都逐个地将它覆盖的每一条树边置成绿色,时间复杂度为 O(nm)。

考虑用差分优化。对于每一条非树边,在其树上深度较小的端点处打上 -1 标记,在其树上深度较大的端点处打上 +1 标记,然后 O(n) 求出每个点的子树内部的标记和。

对于一个点 u,其子树内部的标记之和等于覆盖了 u 和 fa_u 之间的树边的非树边数量。若这个值等于 0,则 u 和 fa_u 之间的树边是 桥。

void TJ(int u, int fa){
	dfn[u] = ++cnt, low[u] = dfn[u]; sta.push(u);
	for(auto v : edge[u]) if(v != fa)/*防止回环*/{
		if(!dfn[v]) TJ(v, u);
		low[u] = min(low[u], low[v]);
	}
	if(dfn[u] == low[u]){//??? 
		co[++col].push_back(u);
		while(sta.top() != u) {
			int x = sta.top();
			scc[x] = col;
			co[col].push_back(x), sta.pop();
		}
		scc[u] = col;
		sta.pop();
	}
}//我自创的优美边双写法

点双的求法 模板

题解 (个人感觉题解写得就很好)
oi-wiki

可能有且只有割点可能同时存在两个点双图中。

每遇到一个割点(见割点的求解方法),就弹出一些点放到一个点双中。

void TJ(int u, int rt) {
	dfn[u] = low[u] = ++ tot;
	if(u == rt && !edge[u].size()) //孤立点的特判 
		scc[++ col].push_back(u);
	stk[++ tp] = u;
	for(auto v : edge[u]) {
		if(!dfn[v]) {
			TJ(v, rt);
			low[u] = min(low[u], low[v]);
			if(low[v] >= dfn[u]) {//点u一定为割点 
				scc[++ col].push_back(u);
				while(stk[tp] != v) {//割点点u不会被弹出  
					scc[col].push_back(stk[tp]);
					-- tp;
				} 
				scc[col].push_back(stk[tp]);
				-- tp;				
				//注意到 if(low[v] >= dfn[u])是在TJ(v)后的,所以实际执行时间比较晚 
				//故弹栈中元素最多只能弹到v,剩下的元素要让之前的某个u(dfn更小的节点)来弹出  
			}
		}
		else low[u] = min(low[u], dfn[v]);//
	}
}

标签:Tarjan,int,连通,笔记,stk,学习,dfn,low,col
From: https://www.cnblogs.com/water-flower/p/18568376

相关文章

  • VUE|学习笔记①
    目录npm创建失败问题解决安装所有依赖如何启动一个vue项目终止项目快捷键自己写一个App组件写一个简单效果vsCode设置自动保存Vue.jsdevtools检查工具最近开始学习一下vue,想要快速入手,也不知道应该看什么课程比较合适,这里我选择看b站上尚硅谷禹神的Vue3教程。这里......
  • VUE|学习笔记②(Vue3核心语法)
     目录OptionsAPI与CompositionAPIsetup概述setup的返回值setup与OptionsAPIsetup语法糖expor报错:应为声明或语句。ts(1128)ref创建:基本类型的响应式数据reactive创建:对象类型的响应式数据ref创建:对象类型的响应式数据ref对比reactivetoRefs与toRef computed......
  • python计算非线性相关的一些笔记
    在Scipy中,相关系数的范围是介于-1到1之间。其中,1表示完全正相关,-1表示完全负相关,0表示无相关关系。因此,相关性最高是1,最低是-1。在计算非线性相关时,样本量的大小对于结果的准确性有重要影响。根据搜索结果,虽然没有一个固定的“最佳”样本量,但是有一些指导性的建议:样本量与p......
  • 强化学习中的Q-Learning
    一、概念        Q-Learning是一种基于值的强化学习方法,用于解决有限马尔可夫决策过程(MarkovDecisionProcess,MDP)。它使模型能够通过采取正确的操作来迭代学习和改进,通过强化学习的状态-动作-奖励-状态-动作形式,训练方案遵循一个模型来采取正确的动作。二、原理 ......
  • 机器学习之集成学习Boosting(Adaboost、提升树、GBDT、XGBOOST)思维导图
    学习笔记—机器学习-集成学习Boosting(Adaboost、提升树、GBDT、XGBOOST)思维导图202411125,以后复习看。(集成学习基础与算法+统计学习方法)集成学习boosting涉及内容很多,书上的内容讲的少。在网上找资料,要结合视频学习,要不根本看不懂。感谢up博主,看了很多遍,讲的认为是全网......
  • CS61B 二叉搜索树的改进:B 树 笔记
    CS61B二叉搜索树的改进:B树笔记笔记的来源:CS61B-2024春季的课程课程主要内容:数据结构与算法分析课程运用语言:Java你可以在我的笔记网站里获得更多有用的资源。这个课有6个Homework,10个Lab,9个Project。其中第一个project是一个完整的2024游戏的实现,很......
  • Vue3.0 实操学习笔记
    安装 node.js安装 https://nodejs.org/en安装后执行node-v查看是否有异常以及npm-v查看是否异常调整为淘宝镜像,cnpm-v查看是否异常npminstall-gcnpm--registry=https://registry.npm.taobao.orgVue安装以及安装脚手架 vue-V查看是否异常cnpmi-......
  • 多臂老虎机(强化学习中的探索与利用)
    文章目录一、多臂老虎机问题介绍1.1问题定义1.2形式化表述1.3累积懊悔1.4估计期望奖励二、探索与利用的平衡三、ϵ-贪心算法四、上置信界算法五、汤普森采样算法  多臂老虎机问题,可以被看作简化版的强化学习问题。与强化学习不同,多臂老虎机不存在状态信息,只有......
  • 深度学习入门- 梯度(Gradient)(三)
    一.手算梯度1.计算下列函数的梯度,并写明过程。    理论依据是前面两文学习过的:    链式法则:复合函数的导数可以用构成复合函数的各个函数的导数的乘积表示。    偏导数是多元函数时:将某一个变量定为目标变量,并将其他变量固定为某个值。    ......
  • ansible学习命令总结1
    安装方式:1.yum安装,在epel源中也可以先用yumsearchepel/ansible会显示出需要安装的包,之后可以通过先安装yuminstall包名,有了软件源以后yuminstallansible就可以了。2.pip安装首先安装:yum-yinstallpython-pippython-devel再安装ansible:pipinstallansible 3......