不会Tarjan,难道就不能与邪恶的SCC作斗争了吗?
祭出Kosaraju。
一些变量名的意义:
a[N] 原图的vector存储
b[N] 原图的所有边反向vector存储
s dfs得出的拓扑序列栈
c[[N] 每个点的SCC编号
算法框架:
1.将原图做一遍类似于拓扑的dfs,越早访问的顶点压在一个栈中。
2.不断从栈顶取出一个未访问过的点,对它的反向图再进行dfs,所有它能到达的未访问过的点就是他的SCC。
3.这样就得到的一个图的SCC。
对于2的正确性,应为这个点必被在栈中比它早入栈的点给压入栈,而且在栈中比它更上面的点已经被访问过,所以它在它下面能找到的点都是他的SCC(同时还避免了重复)
CODE
核心的两遍dfs
inline void dfs(int k) { f[k]=0; for (int i=0;i<a[k].size();++i) if (f[a[k][i]]) dfs(a[k][i]); s.push_back(k); } inline void rdfs(int k) { f[k]=0; c[k]=tot; t[tot]++; for (int i=0;i<b[k].size();++i) if (f[b[k][i]]) rdfs(b[k][i]); }
在主程序中只要:
memset(f,true,sizeof(f)); for (i=1;i<=n;++i) if (f[i]) dfs(i); memset(f,true,sizeof(f)); for (i=s.size()-1;i>=0;--i) if (f[s[i]]) ++tot,rdfs(s[i]);
复杂度为O(|V|+|E|) 虽然跑得没有Tarjan快,但是个人认为很容易理解。