强连通分量

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

强连通分量(Tarjan算法)_i++

强连通分量(Tarjan算法)_强连通分量_02

 

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

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

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

 

强连通分量(Tarjan算法)_#include_03

算法流程 :

强连通分量(Tarjan算法)_i++_04

强连通分量(Tarjan算法)_强连通分量_05

强连通分量(Tarjan算法)_i++_06

 模板 :  ​​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;

}