zoukankan      html  css  js  c++  java
  • 强连通分量

    一、基本概念

    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
    ",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
  • 相关阅读:
    K好数
    最大最小公倍数
    十六进制转十进制
    利用malloc定义数组
    01字串
    ubuntu 14.04 下jdk和sdk+eclipse 的配置
    Mysql3
    求最大连续子串
    UC笔试
    java实现随机洗牌算法
  • 原文地址:https://www.cnblogs.com/2018zxy/p/10364323.html
Copyright © 2011-2022 走看看