zoukankan      html  css  js  c++  java
  • 图论_连通_连通分量


              强连通图 : 强连通分量就是本身
        有向图 --->
               非强连通图 : 多个强连通分量
    图--->
              连通图 : 连通分量就是本身
        无向图 --->
              非连通图 : 多个连通分量

    路径 :          顾名思义.

    路径长度 :  路径上边的数量.

    路径 : 顾名思义.
    路径长度 : 路径上边的数量.

    连通 : 无向图顶点A可以到顶点B,则称A,B连通.
    强连通 : 有向图中,两个顶点间至少存在一条互相可达路径,则两个顶点强连通

    连通图 : 图中任意两点都连通的图.
    强连通图 : 有向图的任意两点都强连通.

    连通分量 : 无向图的极大连通子图称为连通分量.连通图只有一个连通分量,即自身
    强连通分量: 强连通图有向图的极大强连通子图.强连通图的强连通分量只有一个,即强连通图本身.

    基图 : 将有向图的所有边替换成无向边形成的图.
    弱连通图 : 基图是连通图的有向图.(即,连通的有向图)


    求图的连通分量的目的,
    是为了确定从图中的一个顶点是否能到达图中的另一个顶点,
    也就是说,
    图中任意两个顶点之间是否有路径可达。

    求强连通分量有多种算法.

    我用的Tarjan算法. 复杂度O(V+E)

    这两个博客写得不错: 

       https://www.cnblogs.com/reddest/p/5932153.html

       https://www.cnblogs.com/shadowland/p/5872257.html

    int  dfn[16];       // 时间戳
    int  dfn_num = 0;   // 时间 
    int  low[16];       // 节点u所能访问到的最小时间戳 
    
    int inSt[16];       // 节点u是否在栈中.
    
    int st[16];
    int top = 0; 
    
    // 我们维护的信息.
    int col[16];        // 给节点染色, 同一个连通块的节点应该是同一个颜色的.
    int col_num = 0;    // 颜色值.
    int size[16];       // 每个颜色值所拥有的块数. 
    
    /*
     
    第一步:   访问当前节点的所有子节点:   子节点有三种 
        第一种:   未访问过的, 我们对它进行访问, 同时设置它的时间戳dfn[u]和low[u]为++ndfn_num,以及进栈.
        第二种:   访问过的,并且在栈中,我们直接更新我们 当前 节点的low[] --> 注意 应该用low[u] 和 dfn[v]比较. 
        第三种:   访问过的,并且不在栈中的, 我们直接跳过.因为这个时候,所以它已经染色了,属于一个连通块了. 
    第二步:   如果dfn[u] == low[u] 说明 已经找到一个连通块了.
              这时候我们要将栈顶元素弹出,直到当前节点. 记得也要修改inSt, 同时维护我们需要的信息. 
    */
    
    void Tarjan(int u) {
        int v, i;
        dfn[u] = low[u] = ++dfn_num; //添加时间戳. 
        st[++top] = u;      // 进栈 
        inSt[u] = true;     // 标示在栈 
        for (i=head[u]; i; i=edge[i].lst) {
            v = edge[i].to;
            if (!dfn[v]) {
                Tarjan(v);
                low[u] = min(low[u], low[v]);
            } else if (inSt[v]) {
                low[u] = min(low[u], dfn[v]);
            }
        }
        if (dfn[u] == low[u]) { 
            col_num++;
            do {
                inSt[st[top]] = false;
                 col[st[top]] = col_num;
                 size[col_num]++;
            } while (st[top--] != u);
        }
    }
    View Code

    加上2个板子题.

    http://codevs.cn/problem/1332/ 

    题目很简单: 要你求出最大的强连通块,如果有多个则输出字典序最小的一个.

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    
    using namespace std;
    
    const int maxn = 5e4+500;
    
    struct Edge {
        int lst;
        int to;
    }edge[maxn<<1];
    int head[maxn];
    int qsz = 1;
    
    inline void add(int u, int v) {
        edge[qsz].lst = head[u];
        edge[qsz].to  = v;
        head[u] = qsz++;    
    }
    
    int  dfn[maxn];       // 时间戳
    int  dfn_num = 0;   // 时间 
    int  low[maxn];       // 节点u所能访问到的最小时间戳 
    
    int inSt[maxn];       // 节点u是否在栈中.
    
    int st[maxn];
    int top = 0; 
    
    // 我们维护的信息.
    int col[maxn];        // 给节点染色, 同一个连通块的节点应该是同一个颜色的.
    int col_num = 0;    // 颜色值.
    int size[maxn];       // 每个颜色值所拥有的块数. 
    
    int id[maxn];
    
    void Tarjan(int u) {
        int v, i;
        dfn[u] = low[u] = ++dfn_num; //添加时间戳. 
        st[++top] = u;      // 进栈 
        inSt[u] = true;     // 标示在栈 
        for (i=head[u]; i; i=edge[i].lst) {
            v = edge[i].to;
            if (!dfn[v]) {
                Tarjan(v);
                low[u] = min(low[u], low[v]);
            } else if (inSt[v]) {
                low[u] = min(low[u], dfn[v]);
            }
        }
        if (dfn[u] == low[u]) { 
            col_num++;
            id[col_num] = u;
            do {
                inSt[st[top]] = false;
                col[st[top]] = col_num;
                size[col_num]++;
                id[col_num] = min(id[col_num], st[top]);
            } while (st[top--] != u);
        }
    }
    
    int main()
    {
        memset(id, 0x3f, sizeof(id));
        int n, i, u, v, m, t;
        scanf("%d%d", &n, &m);
        for (i=1; i<=m; ++i) {
            scanf("%d%d%d", &u, &v, &t);
            add(u, v);
            if (t==2) add(v, u);
        }
        for (i=1; i<=n; ++i)
            if (!dfn[i]) Tarjan(i);
        
        int mm = 0, tcol = -1;
        for (i=1; i<=col_num; ++i) 
            if (mm < size[i]) {
                mm = size[i];
                tcol = i;
            } else if (m == size[i]) {
                if (id[tcol] > id[i]) 
                    tcol = i;
            }
    //    printf("%d 
    ", tcol);
        printf("%d
    ", mm);
        for (i=1; i<=n; ++i) 
            if (col[i] == tcol) printf("%d ", i);
        printf("
    ");
        
        
        return 0;
    }
    View Code

    https://vjudge.net/problem/HYSBZ-1051

    题目: 求出所有牛都欢迎的牛的个数.  我们可以把所有连通块求出,然后把一个连通块看成一个点,即缩点.  然后找到出度为零的点(连通块), 如果有且只有一个,那么连通块的点数就是答案,否则答案为零.

    #include <cstdio>
    #include <algorithm>
    
    using namespace std;
    
    struct Edge {
        int lst;
        int to;
    }edge[50500];
    int head[10100];
    int qsz = 1;
    
    inline void add(int u, int v) {
        edge[qsz].lst = head[u];
        edge[qsz].to  = v;
        head[u] = qsz++;    
    }
    
    int  dfn[10100];       // 时间戳
    int  dfn_num = 0;   // 时间 
    int  low[10100];       // 节点u所能访问到的最小时间戳 
    
    int inSt[10100];       // 节点u是否在栈中.
    
    int st[10100];
    int top = 0; 
    
    // 我们维护的信息.
    int col[10100];        // 给节点染色, 同一个连通块的节点应该是同一个颜色的.
    int col_num = 0;    // 颜色值.
    int size[10100];       // 每个颜色值所拥有的块数. 
    
    /*
     
    第一步:   访问当前节点的所有子节点:   子节点有三种 
        第一种:   未访问过的, 我们对它进行访问, 同时设置它的时间戳dfn[u]和low[u]为++ndfn_num,以及进栈.
        第二种:   访问过的,并且在栈中,我们直接更新我们 当前 节点的low[] --> 注意 应该用low[u] 和 dfn[v]比较. 
        第三种:   访问过的,并且不在栈中的, 我们直接跳过.因为这个时候,所以它已经染色了,属于一个连通块了. 
    第二步:   如果dfn[u] == low[u] 说明 已经找到一个连通块了.
              这时候我们要将栈顶元素弹出,直到当前节点. 记得也要修改inSt, 同时维护我们需要的信息. 
    */
    
    void Tarjan(int u) {
        int v, i;
        dfn[u] = low[u] = ++dfn_num; //添加时间戳. 
        st[++top] = u;      // 进栈 
        inSt[u] = true;     // 标示在栈 
        for (i=head[u]; i; i=edge[i].lst) {
            v = edge[i].to;
            if (!dfn[v]) {
                Tarjan(v);
                low[u] = min(low[u], low[v]);
            } else if (inSt[v]) {
                low[u] = min(low[u], dfn[v]);
            }
        }
        if (dfn[u] == low[u]) { 
            col_num++;
            do {
                inSt[st[top]] = false;
                 col[st[top]] = col_num;
                 size[col_num]++;
            } while (st[top--] != u);
        }
    }
    
    bool ou[10010];
    
    int main()
    {
    //    freopen("E:\input.txt", "r", stdin);
        int n, i, j, u, v, m;
        scanf("%d%d", &n, &m);
        for (i=1; i<=m; ++i) {
            scanf("%d%d", &u, &v);
            add(u, v);
        }
        for (i=1; i<=n; ++i) 
            if (!dfn[i])  
                Tarjan(i);
                
        // 缩点操作
        int cnt = 0, res = 0;
        for (i=1; i<=n; ++i) {
            if (ou[col[i]]) continue;
            for (j=head[i]; j; j=edge[j].lst) {
                v = edge[j].to;
                if (col[i] != col[v]) {
                    ou[col[i]] = true;
                    break;
                }
            }
        }
        for (i=1; i<=col_num; ++i) {
            if (!ou[i]) {
                res = size[i];
                cnt++;
            }
            if (cnt > 1) {
                res = 0;
                break;
            }
        }
        printf("%d
    ", res);
        
        return 0;
    }
    View Code
  • 相关阅读:
    cmake Found package configuration file but it set OpenCV_FOUND to FALSE
    pthread库"timespec"结构体重定义解决
    【Windows10】运行软件后,窗口不显示的解决办法
    Windows下Cmake生成动态库install命令失败、导入库概念
    【转载】多尺度增强算法Retinex算法(MSRCR)的原理、实现及应用
    【转载】Ubuntu 和 Windows 之间进行远程访问和文件互传
    博客园Markdown编辑器
    合并两个有序数组(C++)
    从协方差矩阵的估算领会MATLAB矩阵编程思维
    常见排序算法的性能对比
  • 原文地址:https://www.cnblogs.com/cgjh/p/9551602.html
Copyright © 2011-2022 走看看