有向图强连通分量
详情请见刘汝佳<<训练指南>>P319
有向图的强连通分量有两个算法,第一个是Kosaraju算法,该算法可以以有向图的每个强连通分量的拓扑顺序给每个强连通分量标记序号.具体代码如下: (G存原图,G2存逆图)
#include<cstdio> #include<cstring> #include<algorithm> #include<vector> using namespace std; const int maxn=1000+10; int n,m; vector<int> G[maxn], G2[maxn];//G存原图,G2存逆图 vector<int> S; int vis[maxn],sccno[maxn],scc_cnt; void dfs1(int u) { if(vis[u]) return ; vis[u]=1; for(int i=0;i<G[u].size();i++)dfs1(G[u][i]); S.push_back(u); } void dfs2(int u) { if(sccno[u]) return; sccno[u]=scc_cnt; for(int i=0;i<G2[u].size();i++)dfs2(G2[u][i]); } void find_scc(int n) { scc_cnt=0; S.clear(); memset(vis,0,sizeof(vis)); memset(sccno,0,sizeof(sccno)); for(int i=0;i<n;i++)dfs1(i); for(int i=n-1;i>=0;i--) if(!sccno[S[i]]){scc_cnt++; dfs2(S[i]);} } int main() { while(scanf("%d%d",&n,&m)==2&&n) { for(int i=0;i<n;i++) { G[i].clear(); G2[i].clear(); } while(m--) { int u,v; scanf("%d%d",&u,&v); G[u].push_back(v); G2[v].push_back(u); } find_scc(n); for(int i=0;i<n;i++) printf("%d号点属于%d分量\n",i,sccno[i]); } } /* 刘汝佳 训练指南P319测试图 输入: 12 17 0 1 1 2 1 3 1 4 4 1 2 5 5 2 4 5 4 6 5 7 6 7 8 6 6 9 9 8 7 10 10 11 11 9 输出: 0号点属于1分量 1号点属于2分量 2号点属于4分量 3号点属于3分量 4号点属于2分量 5号点属于4分量 6号点属于5分量 7号点属于5分量 8号点属于5分量 9号点属于5分量 10号点属于5分量 11号点属于5分量 */
第二个求有向图强连通分量的算法是Tarjan算法.它的时间复杂度也是线性的,不需要计算图的逆且算法常数更小.具体代码如下:
#include<cstdio> #include<cstring> #include<algorithm> #include<vector> #include<stack> using namespace std; const int maxn=1000+10; int n,m; vector<int> G[maxn]; int pre[maxn],low[maxn],sccno[maxn],dfs_clock,scc_cnt; stack<int> S; void dfs(int u) { pre[u]=low[u]=++dfs_clock; S.push(u); for(int i=0;i<G[u].size();i++) { int v=G[u][i]; if(!pre[v]) { dfs(v); low[u]=min(low[u],low[v]); } else if(!sccno[v]) { low[u]=min(low[u],pre[v]); } } if(low[u] == pre[u]) { scc_cnt++; while(true) { int x=S.top(); S.pop(); sccno[x]=scc_cnt; if(x==u) break; } } } void find_scc(int n) { scc_cnt=dfs_clock=0; memset(sccno,0,sizeof(sccno)); memset(pre,0,sizeof(pre)); for(int i=0;i<n;i++) if(!pre[i]) dfs(i); } int main() { while(scanf("%d%d",&n,&m)==2&&n) { for(int i=0;i<n;i++) G[i].clear(); while(m--) { int u,v; scanf("%d%d",&u,&v); G[u].push_back(v); } find_scc(n); for(int i=0;i<n;i++) printf("%d号点属于%d分量\n",i,sccno[i]); } } /* 刘汝佳 训练指南P319测试图 输入: 12 17 0 1 1 2 1 3 1 4 4 1 2 5 5 2 4 5 4 6 5 7 6 7 8 6 6 9 9 8 7 10 10 11 11 9 输出: 0号点属于5分量 1号点属于4分量 2号点属于2分量 3号点属于3分量 4号点属于4分量 5号点属于2分量 6号点属于1分量 7号点属于1分量 8号点属于1分量 9号点属于1分量 10号点属于1分量 11号点属于1分量 */