zoukankan      html  css  js  c++  java
  • 强连通分量、割点、桥

    一、强连通分量

        在有向图G中,如果任意两个不同的顶点相互可达,则该有向图为强连通的;一个有向图G的极大连通子图称为G的强连通分支;将有向图G的每一条边反向形成的图称为G的转置 Gt.

    1.1 有向图的一些定理
    1. 有向无环图中唯一出度为0的点,一定可以由任何点出发到达 
          图中节点的数目为有限多个,而且无环,因此从任何点出发往前走,一定终止于出度为0的点,否则走N+1步(N为图中节点的数目),则必然有一个点被走了两次,形成了环,与无环矛盾!

    2. 有向无环图中所有入度不为0的点,一定可以由某个入度为0的点出发可达 
          由于无环,所以从任何入度不为0的点往回走,必然终止于一个入度为0的点。

    1.2 强连通分量的求解

        一个有向图G可以存在多个极大连通子图,即多个强连通分支。求一个有向图的强连通分支的方法主要有:Korasaju算法和Tarjan算法。

    1.2.1 Korasaju算法 
    1. 深度优先遍历图G,算法每个节点u的结束时间f[u],起点如何选择无所谓; 
    2. 深度优先遍历G的转置图Gt,选择遍历的起点的时候,按照节点的结束时间从大到小进行。遍历的过程中,一边遍历,一边给节点做分类标记,每找到一个新起点,分类标记就加1; 
    3. 第2步中产生的标记值相同的节点构成深度优先森林中的一棵树,也即一个强连通分量

    1.2.2 Tarjan算法 
    1. 做一遍DFS,用dfn[i]表示编号为i的节点在DFS过程中的访问序号(也可以叫做开始时间)。DFS过程中会形成一棵搜索树,在搜索树上越先遍历到的节点,dfn的值就越小。dfn值越小的节点,就称为越“早”。 
    2. low[i]表示从i节点出发DFS过程中i下方节点(开始时间大于dfn[i],且由i可达的节点)所能到达的最早的节点的开始时间。初始时 low[i] = dfn[i]. 
    3. DFS过程中,碰到哪个节点,就将哪个节点入栈。栈中节点只有在其所属的强连通分量已经全部求出时,才会出栈。 
    4. 如果发现某节点u有边连接到栈里的节点v,则更新u的low值为 min(low[u], dfn[v])。若low[u]被更新为dfn[v],则表明目前发现u可达的最早的节点是v。 
        (1)对于u的子节点v,从v出发进行的DFS结束回到u时,使得low[u] = min(low[u], low[v])。因为u可达v,所以v可达的最早的节点,也是u可达的。 
        (2)如果一个节点u,从其出发进行的DFS已经全部完成并回到u,而且此时 low[u] == dfn[u],说明u可达的所有节点,都不能到达任何比u早的节点——那么,该节点u就是一个强连通分量在DFS搜索树中的根。此时,显然栈中u上方的节点,都是不能到达比u早的节点的。将栈中节点弹出,一直弹到u(包括u),弹出的节点就构成了一个强连通分量。

    void Tarjan(int u){
        dfn[u] = low[u] = ++index;
        stack.push(u);
        for each(u,v) in E{
            if (v is not visited){
                Tarjan(v);
                low[u] = min(low[u], low[v]);
            }else if (v in stack){
                low[u] = min(low[u], dfn[v]);
            }
        }
        if (dfn[u] == low[u]){ //u是一个强连通分量的根
            repeat
                v = stack.pop
                print v
            until (u == v)
        }//退栈,把整个强连通分量都弹出来
    }
    

    二、割点和桥

        无向连通图中,如果删除某点后,图变成不连通,则称该点为割点 
        无向连通图中,如果删除某边后,图变成不连通,则称该边为

    求割点和桥的Tarjan算法

        思路和求有向图的强连通分量类似,在深度优先遍历整个图过程中形成一棵搜索树。定义dfn[u]为节点u第一次被访问到的时间(序号),low[u]为u或者u的子树中能够通过非父子边(父子边就是搜索树上的边)追溯到的最早的节点的DFS开始时间。 
        一个顶点u是割点,当且仅当满足(1)或(2) 
    (1)u为树根,且u有多于一个子树 
    (2)u不为树根,且存在(u,v)为树枝边(或称父子边,即u为v在搜索树中的父亲),使得 dfn(u) <= low(v)

        一条边(u,v)是桥,当且仅当 
    (u,v)为树枝边,且满足dfn(u) < low(v)。注意,(u,v)不能有重边。

    Tarjan算法在DFS过程中判断割点或桥

    void Tarjan(u){
       dfn[u] = low[u] = ++index;
       for each(u,v) in E{
            if (v is not visited){
                Tarjan(v)
                low[u] = min(low[u], low[v])
                if(d[u] < low[v])
                    (u,v)为桥
            }else if (v 不是 u的父节点){
                low[u] = min(low[u], dfn[v]);
            }
        }
        if (u 为根)
            u是割点 <=> u 有至少两个子节点
        else
            u是割点 <=> u 有一个子节点v,满足 dfn[u] <= low[v]
    }
    

    Tarjan算法之后判断割点或桥 
        可以先用Tarjan进行dfs算出所有点的low和dfn值,并记录dfs过程中每个点的父节点,然后再把所有点看一遍,看起low和dfn,以找出割点和桥。 
        找桥的时候,需要注意有无重边,若有重边,则不是桥。

    三、无向连通图点双连通分支

        无向连通图的点双连通分支是指不包含割点的极大连通子图。 
    1. 建立一个栈,存储当前双连通分支,搜索图时,每找到一条树枝边或反向边(连到树中祖先的边),就把这条边加入栈中。 
    2. 如果遇到某树枝边(u,v)满足dfn[u] <= low[v],说明u是一个割点,此时把边从栈顶一个个取出,直到遇到了边(u,v),取出的这些边与其关联的点,组成一个点双连通分支。 
    3. 割点可以属于多个点双连通分支,其余点和每条边只能属于一个点双连通分支

    四、无向连通图边连通分支

        无向连通图的边双连通分支是指不包含桥的极大连通子图。 
    求无线连通图的边连通分支,只需要在求出所有的桥以后,把桥边删除,原图就变成了多个连通块,则每个连通块就是一个边连通分支。 
        桥不属于任何一个边连通分支,其余的边和每个顶点都属于且只属于一个边连通分支。

  • 相关阅读:
    【HDU 5750】Dertouzos(数学)
    【OpenJ_POJ C16D】Extracurricular Sports(构造,找规律)
    【CodeForces 266C】Below the Diagonal(模拟)
    【CodeForces 261B】Maxim and Restaurant(DP,期望)
    【ACdream 1187】Rational Number Tree(树,递归)
    A1231. Crash的数字表格(贾志鹏)
    [精准]圆周率
    poj1743 Musical Theme
    3343: 教主的魔法[分块]
    [HNOI2008]玩具装箱toy
  • 原文地址:https://www.cnblogs.com/gtarcoder/p/4869433.html
Copyright © 2011-2022 走看看