Tarjan学习笔记

Tarjan 算法,研究图的连通性。(以下按照算阶上的顺序整理)


无向图

割点

对于给定无向连通图,删除其中一个节点以及所有与其关联的边之后,图不再连通,则称此节点为无向图的割点。

割边(桥)

对于给定无向连通图,删除其中一条边,图不再连通,则称此边为无向图的割边(桥)。

追溯值

low 数组储存, low[x] 为搜索树中以 x 为根的子树中的节点和通过一条不在搜索树上的边可以到达以 x 为根的子树的节点的点的时间戳的最小值。

Tarjan求割边

割点判定法则:对于 x 在搜索树上的一个子节点 y,存在 dfn[x]<low[y],那么 (x,y) 为图的一条割边。

void tarjan(int x,int las)
{
	dfn[x]=low[x]=++now;
	for(int i=lk[x];i;i=nxt[i])
	{
		int y=to[i];
		if(!dfn[y])
		{
			tarjan(y,i^1);
			low[x]=min(low[x],low[y]);
			if(low[y]>dfn[x])//
			{
				ans[++z]={min(x,y),max(x,y)};//存割边
			}
		}
		else if(i!=las)low[x]=min(low[x],dfn[y]);
	}
}

边双连通分量

先跑一遍 Tarjan 标记所有割边,再在不经过割边的情况下 dfs,每个连通块是一个边双连通分量。

Tarjan 求割点

割点判定法则: 对于结点 \(x\),若其不是搜索树的根节点,则 \(x\) 为搜索树的个点当且仅当存在 \(x\) 的一个子结点 \(y\),满足 \(dfn_x\le low_y\)

代码和上面只有一点点不同。

void tarjan(int x)
{
	dfn[x]=low[x]=++now;int flag=0;//计数
	for(int i=lk[x];i;i=nxt[i])
	{
		int y=to[i];
		if(!dfn[y])
		{
			tarjan(y);
			low[x]=min(low[x],low[y]);
			if(low[y]>=dfn[x])//满足条件
			{
				++flag;//
				if(x!=root||flag>1)//
				{
					if(!cut[x])++z;//
					cut[x]=true;//
				}
			}
		}
		else low[x]=min(low[x],dfn[y]);//与 x 相连的点的 dfs 序都可以用来更新 low x
	}

点双连通分量

点双联通分量的缩点并不是非常直观,需要从每个割点发出多条边连接它所属的每一个点双代表的点。所以用圆方树。原图上每个点为圆点,每个点双连通分量新建一个方点,点双中所有的点与此方点连边,得到一棵树,这棵树叫做圆方树。它有非常多美好的性质,比如任意一条路径都是圆点方点交错的,非叶的圆点都是割点等等。


有向图

Tarjan 算法在有向图上最多的应用是求强连通分量

强连通分量

一张图中所有的点都可以两两互达,称这张图为强联通的。有向图的极大强联通子图称为强连通分量。
判定:

void tarjan(int x)
{
	dfn[x]=low[x]=++now;
	st[++tp]=x;ins[x]=true;
	for(int i=lk[x];i;i=nxt[i])
	{
		int y=to[i];
		if(!dfn[y])
		{
			tarjan(y);
			low[x]=min(low[x],low[y]);
		}
		else if(ins[y])low[x]=min(low[x],dfn[y]);
	}
	if(dfn[x]==low[x])
	{
		++cnt;int y;
		do{
			y=st[tp--];ins[y]=false;
			c[y]=cnt;ac[c[y]]+=a[y];
		}while(x!=y);
	}
}
posted @ 2025-02-17 09:00  baiguifan  阅读(45)  评论(0)    收藏  举报