欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

图论-有向图中的强连通片

程序员文章站 2022-06-07 15:14:37
...

概念:
强连通图:在有向图中,如果图中任何两个顶点Vi到Vj有路径,且Vj到Vi也有路径,则称G为强连通图
强连通分量:有向图G的极大强连通子图称为G的强连通分量
极大强连通子图:该子图是图G的强连通子图,如果再加入一个顶点,该子图不再是强连通的。
图论-有向图中的强连通片
1、先求图G的拓扑排序序列
2、按照拓扑排序序列对rG进行深搜,把能到达的所有节点划分在一个强连通分量内,并进行标号
3、输出标号的大小即是强连通分量个数
图论-有向图中的强连通片

①因为第二次深搜对转置图rG进行深搜,所以需要对拓扑排序序列进行逆序
②在GT一开始先将C1作为第一个强联通分量进行搜索,因为其入度为2出度为0所以其不会搜到C2和C3,再搜C2,即使有的边可以到达到C1但是C1的节点都会标记已访问过,所以划清每个强连通分量的界限,不会造成混乱

Code:

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
vector<vector<int>>G,rG;//正向图  反向图 
vector<bool>visited;//是否访问过标记 
vector<int>postOrder;//拓扑排序序列 
vector<int>sccNo;//每个节点所划分的强连通片的编号 
int sccCnt=0;
int N,M,sum=0;
void dfs1(int x){//深搜 =>拓扑排序 
	visited[x]=true;
	for(auto i : G[x])
		if(!visited[i]) 
		   dfs1(i);
	postOrder.push_back(x);
}
void dfs2(int x){//将此节点所能达到的节点 划分为一个强连通片 强连通片内所有节点都可达 
	sccNo[x]=sccCnt;
	for(auto i : rG[x])
		if(sccNo[i]==0)
			dfs2(i);
}
void kosaraju(){
	for(int i=0;i<N;i++){//把所有的边都访问一遍  求出拓扑排序序列 
		if(!visited[i])
			dfs1(i);
	}
	reverse(postOrder.begin(),postOrder.end());//因为在rG里面搜所以对拓扑排序进行逆序
	for(auto x:postOrder){//划分强连通片 
		if(sccNo[x]==0){
			++sccCnt;
			dfs2(x);
		}
	}
} 
int main(){
	cin>>N>>M;
	G.resize(N);
	rG.resize(N);
	visited.resize(N);
	sccNo.resize(N);
	while(M--){
		int x,y;
		cin>>x>>y;
		G[x].push_back(y);
		rG[y].push_back(x);//反向图(转置图) 
	}	
	kosaraju();
	for(int i=0;i<N;i++){
		cout<<char(i+'A')<<" : "<<sccNo[i]<<endl;
	}
	cout<<sccCnt;
	return 0;
} 
//测试数据:
//7 13
//0 1
//0 2
//0 5
//1 2
//1 3
//3 0
//3 2
//4 2
//4 6
//5 0
//5 2
//6 3
//6 4


部分图片采用 《算法设计与分析》(黄宇 编著)