• 图的强连通&双连通


    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

  • 相关阅读:
    [SDOI 2009] HH去散步
    [SDOI 2017] 新生舞会
    【期望 数学】7.6神经衰弱
    初涉2-SAT
    【tarjan 拓扑排序 dp】bzoj1093: [ZJOI2007]最大半连通子图
    【贪心】bzoj1572: [Usaco2009 Open]工作安排Job
    【贪心优化dp决策】bzoj1571: [Usaco2009 Open]滑雪课Ski
    概述「并查集补集转化」模型&&luoguP1330 封锁阳光大学
    初涉tarjan缩点
    初涉三元环
  • 原文地址:https://www.cnblogs.com/wenruo/p/4989425.html
Copyright © 2020-2023  润新知