tarjan强连通、缩点相关

本文介绍了如何利用tarjan算法在O(n)的时间复杂度内找到有向图的强连通分量。解释了算法的思想,即通过dfs遍历图,通过dfn和low数组记录节点的遍历顺序和最低可达点,利用栈来判断是否形成强连通分量。文章还提及了tarjan缩点的概念,即将强连通分量视为一个超级点,以简化图的结构。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

tarjan求强连通分量

思想

普通的tarjantarjantarjan就是用来找有向图的强连通分量
也就是在一个有向图中找一个尽可能大的图使其中的每一个点都可到达图中其他任意一点
这个东西tarjantarjantarjan可以O(n)O(n)O(n)时间dfsdfsdfs一遍求出

对于图的dfsdfsdfs序,如果有一条有向边指向已遍历过的点,那么这个点到它的当前这个儿子节点形成强连通

就像这样,1,3,21,3,21,3,2形成强连通

但是只是强连通而已,不一定是强连通分量
如果一个节点的子孙有指向它的祖先节点的边,那么一定可以形成一个更大的强连通图
换句话说,如果一个节点的子孙没有指向它的祖先的边、只有指向它的边,那么这个点和它的子孙构成强连通分量

就像这样,蓝色的是强连通图,红色的却是强连通分量

那么我们只需要知道一个节点的子孙中有没有连回它祖先就可以了
那么就用一个栈记录一下


具体实现

具体实现要弄几个数组来搞tarjantarjantarjan

  • dfn[x]dfn[x]dfn[x]表示xxx点在dfsdfsdfs中是第几个被遍历到的

  • low[x]low[x]low[x]表示xxx点与它的子孙节点中,dfndfndfn最小的值

  • stack[x]stack[x]stack[x]表示当前所有可能构成强连通分量的点

  • bz[x]bz[x]bz[x]表示xxx点是否在stackstackstack

那么一个tarjantarjantarjan过程是这样的

  • 首先dfn[x]=low[x]=++totdfn[x]=low[x]=++totdfn[x]=low[x]=++tot,并把xxx入栈

  • xxx能到达的所有点,如果没有访问过,那就dfsdfsdfs该点,并把low[x]low[x]low[x]取两者的minminmin

  • 如果访问过且该点在栈中那么low[x]low[x]low[x]也取两者的minminmin

  • 最后如果dfn[x]==low[x]dfn[x]==low[x]dfn[x]==low[x],说明子孙节点和xxx构成强连通分量了,给它们全染上同一种颜色就好了

  • 为什么这么做的原因我不想说


code

O3 inline void tarjan(ll x)
{
	dfn[x]=low[x]=++tot;
	bz[x]=1,stack[++top]=x;
	rep(i,x)
	{
		if (!dfn[tov[i]])
		{
			tarjan(tov[i]);
			low[x]=min(low[x],low[tov[i]]);
		}
		else
		{
			if (bz[tov[i]])low[x]=min(low[x],low[tov[i]]);
		}
	}
	if (dfn[x]==low[x])
	{
		color[x]=++sum,bz[x]=0;
		while (stack[top]!=x)
		{
			color[stack[top]]=sum;
			bz[stack[top--]]=0;
		}
		--top;
	}
}

tarjan缩点

如果用tarjantarjantarjan缩点,相当于把某个强连通分量里的点的权值加起来或什么的,看成一个超级点
做完以后整张图通常是一棵树或是一个DAGDAGDAG
这个操作比较重要
具体的自己手玩

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值