对于一个有向图定点的子集,在该子集中任取两点u与v,都能找到一条从u到v的路径,则称该子集是强连通的。若该集合加入到任意点集中,它都不再强连通,则称这个子集是原图的一个强连通分量。任意一张图都可以分解成若干个不相交的强连通分量。这是强连通分量分解。把分解后的强连通分量缩成一个顶点,就可以得到一个有向无环图。
如图:
求一张图的强连通分量的个数,常用tarjan算法,它是基于对图深度优先搜索的算法,每个强连通分量为搜索树中的一棵子树。搜索时,把当前搜索树中未处理的节点加入一个堆栈,回溯时可以判断栈顶到栈中的节点是否为一个强连通分量。
模板如下:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<algorithm> 5 #include<cmath> 6 using namespace std; 7 int n,m; 8 struct node{ 9 int fr,to; 10 }ed[50001]; 11 bool vis[10001]; 12 int dfs[10001],stack[10001],head[10001],low[10005]; 13 int tot=0,ans=0,in=0,num=0; 14 void add(int u,int v){ 15 ed[++num].fr=head[u];head[u]=num;ed[num].to=v; 16 } 17 void tarjan(int x){ 18 dfs[x]=low[x]=++tot; 19 stack[++in]=x; 20 vis[stack[in]]=1; 21 int i; 22 for(i=head[x];i;i=ed[i].fr){ 23 if(!dfs[ed[i].to]){ 24 tarjan(ed[i].to); 25 } 26 if(vis[ed[i].to]){ 27 low[x]=min(low[x],low[ed[i].to]); 28 } 29 } 30 if(low[x]==dfs[x]){ 31 for(++ans;stack[in+1]!=x;) vis[stack[in--]]=0; 32 } 33 } 34 int main(){ 35 scanf("%d%d",&n,&m); 36 int i; 37 for(i=1;i<=m;i++){ 38 int x,y; 39 scanf("%d%d",&x,&y); 40 add(x,y); 41 } 42 for(i=1;i<=n;i++) if(!dfs[i]) tarjan(i); 43 printf("%d\n",ans); 44 return 0; 45 }