强连通分量模板

一:定义

定义:在有向图G中,如果两个顶点u、v互相连通,那么我们称这两个顶点是强连通的

定义:在有向图G中,如果任意两个顶点互相连通,那么我们称这个图是一个强连通图

定义:在有向图G中,如果有几个顶点互相连通,那么我们称这几个顶点组合起来的子图为极大强连通子图,也叫做强连通分量,注意:每一个顶点都可以被作为强连通分量

性质:如果将有向图G中的强连通分量都缩为一个点,可以原图G就可以变成一个DAG(有向无环图)。

二:算法之 Tarjan

Tarjan算法是基于对图的DFS的算法,每个强连通分量为搜索树中的一颗子树

搜索时,把当前搜索树中未处理的节点加入一个,回溯是可以判断栈顶到栈中的节点是否构成一个强连通分量

在搜索时,我们会遇到四种边:

  • 树枝边:搜索树上的边。
  • 前向边:与DFS方向一致,从某个节点指向某个祖先的边。
  • 后向边:与DFS方向相反。
  • 横叉边:从每个节点指向搜索树中另一子树的边。

定义:dfn[u]为节点u的搜索次序编号,low[u]为u或者u的子树能回溯到的最早的栈中的dfn的值

那么,由定义可以得出:

  • 如果(u,v)为树枝边,那么low[u]=min(low[u],low[v])。
  • 如果(u,v)为后向边或者横叉边,那么low[u]=min(low[u],dfn[v]);

为什么要这样做?因为若u为强连通分量的跟,那么它的子孙的最高祖先就应该为它本身。

三:代码实现

还要流程吗?

直接发代码吧。

至于模板题目看这题受欢迎的牛,这里也有我的一篇题解

inline void tarjan(int u)//inline只是提速用的,不用理会。
{
	dfn[u]=low[u]=++index1;//记录搜索次序编号
	st[++top]=u;//将当前搜索到的压入队列;
	vis[u]=1;//用过了。
	for(register int i=head[u];i;i=edge[i].next)//前向星遍历图
	{
		int v=edge[i].to;
		if(!dfn[v]){
			tarjan(v);
			low[u]=min(low[u],low[v]);
		}
		else if(vis[v])
		  low[u]=min(low[u],dfn[v]);
	}
	int now;
	if(low[u]==dfn[u])
	{
		++cnt;//下一个强连通分量;
		do{
			now=st[top--];//将队列顶端弹出
			size[cnt]++;//记录这一个强连通分量所包含的节点
			f[now]=cnt;//缩点,表示成同一个强连通分量
			vis[now]=0;//取消使用
		}while(u!=now);
	}
}

猜你喜欢

转载自blog.csdn.net/guoyangfan_/article/details/82250776
今日推荐