强连通分量(Tarjan算法)

强连通分量

       有向图强连通分量:在有向图G中,如果两个顶点vi,vj间(vi>vj)有一条从vi到vj的有向路径,同时还有一条从vj到vi的有向路径,则称两个顶点强连通。如果有向图G的每两个顶点都强连通,称G是一个强连通图。有向图的极大强连通子图,称为强连通分量

 

        Tarjan 算法是基于对图优先搜素的算法 , 每个强连通分量为搜索树中的一棵子树 .搜索时,把当前搜索树中未处理的节点加入一个堆栈 ,回溯时可以判断栈顶到栈中节点是否为一个强连通分量 . 

         定义 dfn[u] 为节点u搜索的次序编号(时间戳) ,low[u]  为u 或u的子树能够追溯到的最早的栈中节点的次序号 . 

         当dfn[u] = low[u]  时 , 以 u 为根的搜索子树上所有节点时一个强连通分量 , 伪代码 : 

 

算法流程 :

 模板 :  https://www.luogu.org/problemnew/show/P2863

邻接表存图

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <set>
#include <map>
#include <vector>

using namespace std;
const int MAX = 10010 ;
vector <int> g[MAX] ;
int color[MAX] ;
int dfn[MAX] ; // 表示dfs时第几个被搜到的
int low[MAX] ; // 表示这个点以及其子孙节点连的所有点中dfn最小的值
int stack[MAX] ; //当前所有可能能构成强连通分量的点
int vis[MAX] ; // 标记
int cnt[MAX] ;
int deep ,top , n , m, sum , ans ;
void tarjan(int u ){
	dfn[u] = ++deep ;
	low[u] = deep ;
	vis[u] = 1 ;// 记录是否在栈中
	stack[++top] = u ;
	for(int i = 0 ; i<int(g[u].size()) ;i++){
		int v = g[u][i] ; // 邻接的子孙节点  ;
		if(!dfn[v]){
			tarjan(v) ;
			low[u] = min(low[u],low[v]) ;
		}
		else{
			if(vis[v]){
				low[u] = min(low[u],dfn[v]) ;
			}
		}
	}
	if(dfn[u]==low[u]){
		color[u]= ++sum ;
		vis[u] = 0 ;
		while(stack[top]!=u){
			color[stack[top]] = sum ;
			vis[stack[top--]] = 0 ;
		}
		top--  ;
	}

}
int main(){
    
    scanf("%d%d",&n,&m);
	for(int i = 1 ; i <= m ;i++){
		int u , v ;
		scanf("%d%d",&u,&v);
		g[u].push_back(v) ;
	}
	for(int i = 1 ; i<=n ; i++ ){
		if(!dfn[i]){
			tarjan(i) ;
		}
	}
	for(int i = 1 ; i<=n ; i++ ){
		cnt[color[i]]++ ;
	}
	for(int i = 1 ; i<=sum ; i++ ){
		if(cnt[i]>1){
			ans++ ;
		}
	}
	cout<<ans ;
    return 0;

}

链式前向星 

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <set>
#include <map>
#include <vector>
#include <stack>
using namespace std;
const int MAX = 100100 ;
int n , m , idx  ;
int cnt  ,Bcnt  ;
int head[MAX] ;
int vis[MAX] ;
int dfn[MAX] ,low[MAX] ;
int Belong[MAX] ;
stack<int> s ;
struct edge{
	int v ;
	int next ;
}e[MAX] ;
void add(int u , int v){
	e[++cnt].v = v ;
	e[cnt].next = head[u] ;
	head[u] = cnt++ ;
}
void tarjan(int u ){
	int v;
	dfn[u] = low[u] = ++idx ; // 每次dfs , u的次序号加一
	s.push(u) ; // 将u 进栈
	vis[u] = 1 ; // 标记u已经在栈内
	for(int i = head[u] ;i!=-1 ; i= e[i].next ){
		v = e[i].v;
		if(!dfn[v]){ // 如果还没处理过
			tarjan(v) ;
			low[u] = min(low[u],low[v]) ;//如果v在栈内,u点能到达的最小次序号是它自己能到达点的
			// 最小次序号和v的次序号中较小的
		}
		else{
			if(vis[v]){
				low[u] = min(low[u],dfn[v]) ;
			}
		}
	}
	if(dfn[u] == low[u]){
		Bcnt++ ;
		do{
			v = s.top() ;
			s.pop() ;
			vis[v] = 0 ;
			Belong[v] = Bcnt ;
		}while(u!=v) ;
	}
	
	
}
int main(){
	
    cin >> n >> m ;
    memset(head,-1,sizeof(head)) ;
    for(int i = 1 ; i<=m ; i++ ){
    	int u , v;
    	cin >>u >>v ;
    	add(u,v) ;
	}
	for(int i = 1 ; i<=n ; i++){
		if(!dfn[i]){
			tarjan(i) ;
		}
	}
//	cout<<endl;
//	for(int i = 1 ; i<=6 ; i++){
//		cout<<dfn[i] <<" "<<low[i] <<endl ;
//	}
// 	cout<<" 有"<<Bcnt<<" 个强连通分量 "<<endl  ;
// 	for(int i = 1 ; i<=Bcnt; i++ ){
// 		cout<<i<<" : " ;
// 		for(int j = 1 ; j<=n ; j++){
// 			if(Belong[j]==i){
// 				cout<<j <<" " ;
//			 }
//		 }
//		 cout<<endl;
//	 }
	int ans = 0 ;
	for(int i = 1 ; i<=Bcnt ; i++ ){
		int num = 0 ;
		for(int j = 1; j<=n ; j++ ){
			if(Belong[j]==i){
				num++ ;
			}
		}
		if(num>1){
			ans++ ;
		}
	}
	cout<<ans;

    return 0;

}

 

 

 

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值