强连通分量
一、基本概念
1、强连通图:对于有向图来说,任意两点u,v之间都有一条路径,则有向图G是一个强连通图。
2、强连通子图:有向图G’一个强连通图,且是有向图G的一个子图。
3、强连通分量:有向非强连通图的极大强连通子图称为连通分量。
4、极大强连通子图:G是强连通子图且不存在另一个强连通子图G’使得G‘是G的真子集。
二、算法实现
1、kosaraju算法
(1)建立深搜树,对每个节点编号;
(2)对图G进行反向dfs,删除标记到的节点,这些顶点构成一个强连通分量;
(3)统计有多少个顶点就是多少个强连通分量。
实现:
#include<iostream> #include<cstdio> #include<cstring> using namespace std; const int maxn = 1200; int mp[maxn][maxn],vis[maxn],num[maxn],m,n,tot; void Init() { memset(mp,0,sizeof(mp)); memset(vis,0,sizeof(vis)); memset(num,0,sizeof(num)); tot=0; } void dfs1(int u) //正向搜索,给节点编号 { vis[u]=1; for(int i=1;i<=n;i++) if(!vis[i]&&!mp[u][i]) dfs1(i); num[++tot]=u; } void dfs2(int u) //反向深搜 { vis[u]=1; for(int i=1;i<=n;i++) if(!vis[i]&&mp[i][u]) dfs2(i); } void kosaraju() { int i,j,tim=0; for(i=1;i<=n;i++) //建立深搜树 if(vis[i]==0) dfs1(i); memset(vis,0,sizeof(vis)); for(i=n;i>=1;i--) //从标号大的点开始 if(vis[num[i]]==0){ tim++; dfs2(num[i]); } printf("%d\n",tim); } int main(void) { int i,j,x,y; scanf("%d%d",&n,&m); Init(); for(i=0;i<m;i++){ scanf("%d%d",&x,&y); mp[x][y]=1; } kosaraju(); return 0; } /* 5 5 1 2 2 3 3 4 4 2 4 5 */
2、Tarjan算法
(1)建立深搜树,给每个点标号(num数组)
(2)low[u]表示u节点能够回溯到的最早的栈中的节点的num的值。
(3)当num[u]==low[u]时(后序遍历完),表示u节点是这个连通分量的头结点,然后让节点出栈,完成一个强连通分量。
#include<iostream> #include<cstdio> #include<cstring> using namespace std; const int maxn = 1200; int fa[maxn],vis[maxn],num[maxn],low[maxn]; int head[maxn],ver[maxn],next[maxn],tot,tim,col; int st[maxn],top; int MIN(int x,int y) { return x<y?x:y; } void Init() { memset(fa,0,sizeof(fa)); memset(vis,0,sizeof(vis)); memset(num,0,sizeof(num)); memset(low,0,sizeof(low)); memset(head,0,sizeof(head)); memset(next,0,sizeof(next)); memset(ver,0,sizeof(ver)); tot=0;top=0;tim=0;col=0; } void addedge(int u,int v) { ver[++tot]=v;next[tot]=head[u];head[u]=tot; } void Tarjan(int u) { low[u]=num[u]=++tim; st[++top]=u; vis[u]=1; for(int i=head[u];i;i=next[i]){ int v=ver[i]; if(!vis[v]){ Tarjan(v); low[u]=MIN(low[u],low[v]); } else if(!fa[v]) low[u]=MIN(low[u],num[v]); //v还在栈内,不属于任何强连通分量 } if(low[u]==num[u]){ fa[u]=++col; while(st[top]!=u){ fa[st[top]]=col; //st[top]为强连通分量的一个节点 top--; } --top; //头结点u退栈 } } int main(void) { int n,m,i,j,x,y; Init(); scanf("%d%d",&n,&m); for(i=0;i<m;i++){ scanf("%d%d",&x,&y); addedge(x,y); } Tarjan(1); for(i=1;i<=n;i++){ printf("%d ",fa[i]); } return 0; }