Kosaraju算法——强连通分量
´有向图的极大强连通子图,称为强连通分量。
´子图指的是选取V的一个子集V’,以及E当中所有满足u,v∈V’的边集E’所指代的图.
´我们需要找出一幅有向图当中的所有强连通分量。
´一个最朴素的算法:
´构造一个传递闭包(也就是数组Aij表示i能否到达j),然后把Aij=Aji=1的节点置于同一个强连通分量当中
´这个算法的复杂度是O(n^3),优点是代码复杂度小,缺点是速度太慢了
´这个算法原理和上面的方法是类似的,如果A能到达B并且B能到达A,那么A和B在同一个强连通分量里面。
´也可以等同于说,如果原图与逆图中A都能到达B,那么A与B在同一个强连通分量里面。
´逆图,指的是将原本有向图的所有边的方向变成相反,也就是说原本从a到b的边变成从b到a。
´算法流程有三步:
´对原图进行DFS,求出每一个节点的结束时间戳(离开这个节点时访问了多少个节点);
´选择结束时间戳最晚的节点,在逆图中进行遍历,删除能遍历到的节点,这些节点构成一个强连通分量;
´如果还有顶点未删除,重复第二步;
#include <bits/stdc++.h> using namespace std; const int maxn=100000+15; int n,m; vector <int> edge[maxn],rev[maxn]; int tim,rank[maxn],ref[maxn]; bool boo[maxn]; int color[maxn],tot; int dfs1(int now) { if (boo[now]) return 0; boo[now]=true; for (int i=0;i<edge[now].size();dfs1(edge[now][i]),i++); rank[now]=++tim; ref[tim]=now; return 0; } int dfs2(int now) { if (color[now]!=0) return 0; color[now]=tot; for (int i=0;i<rev[now].size();dfs2(rev[now][i]),i++); return 0; } int main() { freopen("ko.in","r",stdin); scanf("%d%d",&n,&m); for (int i=1;i<=m;i++) { int x,y; scanf("%d%d",&x,&y); edge[x].push_back(y); rev[y].push_back(x); } for (int i=1;i<=n;i++) if (!boo[i]) dfs1(i); for (int i=n;i>=1;i--) if (color[ref[i]]==0) { tot++; dfs2(ref[i]); } for (int i=1;i<=n;i++) printf("%d ",color[i]); return 0; }