第一次dfs为后序遍历
第二次dfs将所有边反向,从编号最大的顶点开始(也就是原图缩点后形成的DAG根节点所在的强连通分量),由于边反向后,不能由这个强连通分量访问到其他强连通分量的顶点,而强连通分量内部之间的顶点不受影响,所以每一次dfs所遍历到的所有顶点形成一个强连通分量
通过这种算法得到的强连通分量的编号表示DAG上的一种拓扑序
算法进行了两次dfs,时间复杂度(O(|V|+|E|))
const int maxn=10010,maxm=50010;
int n,m,head[maxn],nxt[maxm],to[maxm],cnt=1;
int rhead[maxn],rnxt[maxm],rto[maxm];
int book[maxn],comp[maxn];
vector<int> vs;
void dfs(int u){
book[u]=1;
for(int i=head[u];i;i=nxt[i]){
int v=to[i];
if(!book[v]) dfs(v);
}
vs.push_back(u);
}
void rdfs(int u,int k){
book[u]=1;
comp[u]=k;
for(int i=rhead[u];i;i=rnxt[i]){
int v=rto[i];
if(!book[v]) rdfs(v,k);
}
}
int scc(){
memset(book,0,sizeof(book));
vs.clear();
for(int i=0;i<n;i++){
if(!book[i]) dfs(i);
}
memset(book,0,sizeof(book));
int k=0;
for(int i=vs.size()-1;i>=0;i--){
if(!book[vs[i]]) rdfs(vs[i],k++);
}
return k;
}