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

  • 相关阅读:
    Typescript+WebGL+Webpack开发环境搭建
    SVG的动态之美-搜狗地铁图重构散记
    2017年个人总结-程序员的中年焦虑症
    上海2017QCon个人分享总结
    CSS预编译与PostCSS以及Webpack构建CSS综合方案
    前端工程师的基本素养
    不仅仅是复制粘贴
    《微信小程序七日谈》- 第七天:不要捡了芝麻丢了西瓜
    《微信小程序七日谈》- 第六天:小程序devtool隐藏的秘密
    《微信小程序七日谈》- 第五天:你可能要在登录功能上花费大力气
  • 原文地址:https://www.cnblogs.com/wenruo/p/4989425.html
Copyright © 2011-2022 走看看