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

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

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

Code:

#include
#include
#include
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


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

你可能感兴趣的:(算法,有向图,算法,图论)