图论之强连通分量

问题描述:给出一个图,求其中的强连通分量。


分析:常见的算法有tarjan算法和二度深搜法(即Kosaraju算法)

这里详解tarjan算法。

算法核心:

对于一个图,我们给每一个dfs访问的点设立时间戳,即到达这里所需的最小时间,那么如果在树中,显然儿子结点的时间戳一定大于父亲的时间戳,而这里是有环的图,所以如果儿子的时间戳小于父亲的时间戳,则说明在访问父亲之前,就已经到达儿子了,那么就存在环。我们用一个栈维护访问的结点,如果当前节点的儿子结点的时间戳的最小值等于当前结点的时间戳,那么就把压在当前节点以上的点弹出,作为一个强连通分量。

具体步骤:其实上面的说法并不准确,在这里再做严格声明:

dfn[i]表示第i结点的时间戳,low[i]表示第i结点所能到达的点集中时间戳的最小值。

那么对于每个i,我们将其压入栈,dfn[i]=++time,然后枚举i所能到达的j,若j未被访问,则接着dfs(j),回溯时low[i]=min(low[i],low[j]),若j已被访问,且j在栈中,则low[i]=min(low[i],dfn[j]).若不在栈中,那么j已在一个强连通分量中了,而没有访问i,说明i不在j的强连通分量中。

也许你会问,那样j就有可能同时存在在两个强连通分量中,会不会没有考虑,你可以这样想:既然j同时可以在这两个强连通分量中,那说明A强连通分量都可以到达j,B亦如此,j可以到达A强连通分量,B亦如此,那么是不是A和B就构成一个更大的强连通分量了呢?


参考程序:

#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=110000;
int n,m;
struct Node{
	int j,next;
}e[maxn];
int a[maxn],ti[maxn],low[maxn];
int stack[maxn],top=0;
int ans=0,TIME=0,NodeCnt=0,CaseCnt=0;
bool instack[maxn];
void tarjan(int u){
	low[u]=ti[u]=++TIME;
	stack[++top]=u;instack[u]=true;
	for (int p=a[u];p;p=e[p].next){
		int v=e[p].j;
		if (!ti[v]){
			tarjan(v);
			low[u]=min(low[u],low[v]);
		}else if (instack[u])low[u]=min(low[u],ti[v]);
	}
	if (low[u]==ti[u]){
		int res=0;
		//CaseCnt++;
		while (stack[top]!=u && top>0)
			instack[stack[top]]=false,res++,top--;
		res++;top--;instack[u]=false;
		ans=max(ans,res);
	}
}
int main(){
	scanf("%d%d",&n,&m);
	for (int i=0;i<m;i++){
		int u,v;
		scanf("%d%d",&u,&v);
		int p=++NodeCnt;
		e[p].j=v;e[p].next=a[u];a[u]=p;
	}
	memset(ti,0,sizeof(ti));
	memset(instack,0,sizeof(instack));
	ans=0;
	for (int i=1;i<=n;i++)
		if (!ti[i])tarjan(i);
	printf("%d",ans);
	return 0;
}

而对于二度深搜法,其核心时建立一个正向一个反向图,如果在正向图中i能到j,反向图中i还能到j,则说明其在同一个强连通分量中。这里就不作详述。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值