zoukankan      html  css  js  c++  java
  • 图的强连通&双连通

    http://www.cnblogs.com/wenruo/p/4989425.html 

    强连通

    强连通是指一个有向图中任意两点v1、v2间存在v1到v2的路径及v2到v1的路径。

    dfs遍历一个图,会生成一颗树。每个节点按最先遍历的时间给定一个编号,用一个数组dfn表示,又叫时间戳。

    然后有几个概念。

    画图举例:

    假设一个边是u-->v

    树边:dfs遍历后生成树的边叫做树边。dfn[u] = -1 如图中<1,2> <2,3> <3,4> <2,5> <1,6> <6,7> <7,8>

    前向边:dfn[u]<dfn[v] 如图中<1,4>

    后向边:dfn[u]>dfn[v] 如图中<4,2>

        前向边和后向边的两点公共最先为其中一点,即u或v中一点。

    横跨边:dfn[u]>dfn[v] 如图中<6,5>

    定义一个数组low用来记录一个结点通过任意条树边和最多一条横向边或向后边,所能到达的最小dfn值。

    当一个结点low[n] == dfn[n] n就是一个强连通的根,即n的子树是一个强连通分量。可知一个强连通分量的dfn值都是连续的。

    可以用一个根唯一的表示一个强连通分量。

    强连通模板:

    //强连通模板(tarjan) (hdu 1269
    const int N = 10005;
    const int M = 100005;
    
    struct Edge {
        int to, next;
    } edge[M];
    int head[N];
    int cnt_edge;
    
    void add_edge(int u, int v)
    {
        edge[cnt_edge].to = v;
        edge[cnt_edge].next = head[u];
        head[u] = cnt_edge;
        cnt_edge++;
    }
    
    int dfn[N];int idx;
    int low[N];
    int stk[N];int top;
    int kind[N];int cnt;
    bool in[N];
    
    int n, m;
    
    void dfs(int u)
    {
        dfn[u] = low[u] = ++idx;
        in[u] = true;
        stk[++top] = u;
        for (int i = head[u]; i != -1; i = edge[i].next)
        {
            int v = edge[i].to;
            if (!dfn[v])
            {
                dfs(v);
                low[u] = min(low[v], low[u]);
            }
            else if(in[v])
                low[u] = min(low[u], dfn[v]);
        }
    
        if (low[u] == dfn[u])
        {
            ++cnt;
            int j;
            do {
                j = stk[top--];
                in[j] = false;
                kind[j] = cnt;
            } while (j != u);
        }
    }
    
    void init()
    {
        memset(dfn, 0, sizeof dfn);
        memset(head, -1, sizeof head);
        cnt_edge = 0;
        top = cnt = idx = 0;
    }
    
    int main()
    {
        while (~scanf("%d%d", &n, &m))
        {
            if (n == 0 && m == 0) break;
            int a, b;
            init();
            for (int i = 0; i < m; ++i)
            {
                scanf("%d%d", &a, &b);
                add_edge(a, b);
            }
    
            for (int i = 1; i <= n; ++i)
            {
                if (!dfn[i]) dfs(i);
            }
    
            if (cnt == 1) puts("Yes");
            else puts("No");
        }
        return 0;
    }
    

      

    双连通

    定义:在无向连通图中,如果删除该图的任何一个结点都不能改变该图的连通性,则该图为双连通的无向图。

    个人理解就是一个双连通图没有割点,没有桥的图。

    在无向图中是没有前向边和横跨边的,只有树边和后向边。

    如何找到割点和桥呢?

    首先对于树根,如果他有多于两个的子结点,该根结点即为割点。

    对于非根节点,画图举例:

    low[v]<dfn[u] low[v]==dfn[u]

    虚线连接到的位置就是low[v],观察可得当low[v]<=dfn[u]时,一旦去掉u点,f和v不再连通。所以当u不是树根时,任意一个子节点v满足low[v]>=dfn[u],u就是割点。

    同时,当low[v]>dfn[u],(u,v)就是桥。

    边的双连通分量比较简单, poj1438 & poj3177

    点的双连通分量, poj2942 & hdu3394

  • 相关阅读:
    Constants and Variables
    随想
    C#基础篇之语言和框架介绍
    Python基础19 实例方法 类方法 静态方法 私有变量 私有方法 属性
    Python基础18 实例变量 类变量 构造方法
    Python基础17 嵌套函数 函数类型和Lambda表达式 三大基础函数 filter() map() reduce()
    Python基础16 函数返回值 作用区域 生成器
    Python基础11 List插入,删除,替换和其他常用方法 insert() remove() pop() reverse() copy() clear() index() count()
    Python基础15 函数的定义 使用关键字参数调用 参数默认值 可变参数
    Python基础14 字典的创建修改访问和遍历 popitem() keys() values() items()
  • 原文地址:https://www.cnblogs.com/wenruo/p/4989425.html
Copyright © 2011-2022 走看看