强连通分量

一、基本概念

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
*/
View Code

 

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;
}
View Code

 

posted @ 2019-02-12 15:26  麟阁  阅读(177)  评论(0编辑  收藏  举报