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. 割点可以属于多个点双连通分支,其余点和每条边只能属于一个点双连通分支

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

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

  • 相关阅读:
    Java RunTime Environment (JRE) or Java Development Kit (JDK) must be available in order to run Eclipse. ......
    UVA 1597 Searching the Web
    UVA 1596 Bug Hunt
    UVA 230 Borrowers
    UVA 221 Urban Elevations
    UVA 814 The Letter Carrier's Rounds
    UVA 207 PGA Tour Prize Money
    UVA 1592 Database
    UVA 540 Team Queue
    UVA 12096 The SetStack Computer
  • 原文地址:https://www.cnblogs.com/gtarcoder/p/4869433.html
Copyright © 2011-2022 走看看