图论-有向图中的强连通片
程序员文章站
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
部分图片采用 《算法设计与分析》(黄宇 编著)