• 有向连通图-强连通分量小结


    流图:存在点r,从r出发可以达到有向图中的每一个点,则该图成为流图

    一些基本概念:

      1.树枝边(x,y):指搜索树中的边,即x是y的父亲

      2.前向边(x,y):指搜索树中x是y的祖先结点

      3.后向边(x,y):指搜索树中y是x的祖先结点

      4.横叉边(x,y):除以上三种情况外的边,可以证明dfn[y]<dfn[x]

    证明:如果dfn[y]>dfn[x],那么x必定比y先访问到,同时从x可以到y,那么这条边就变成了前向边或树枝边

    有向图的强连通分量SCC

    tarjan算法和求无向图的双联通分量不同,需要额外开一个栈,栈中需要保存一下两类结点

      1.搜索树上x的祖先结点,记为anc[x]

        设y是x的祖先,并且有后向边(x,y),则x,y之间有环

      2.已经访问过,并且存在一条路径达到anc[x]的点

        设从z出发可以达到anc[x],并且存在一条横叉边(x,z),那么y->x->z->y就是一个环

    如何求追溯值low

      1.当x第一次被访问时,low[x]=dfn[x],x入栈

      2.遍历以x为起点的所有边(x,y)

        a.如果y没有被访问过,则继续递归,递归结束后low[x]=min(low[x],low[y])

        b.若y被访问过且在栈中,那么low[x]=min(low[x],dfn[y])

      3.遍历结束后判断dfn[x]==low[x],如果是,那么找到了一个强连通分量,把栈中所有在x以上的结点弹出即可

    int c[N],out[N],cnt;//新图的点个数,每个点的出度 
    int ind,dfn[N],low[N],stk[N],top,ins[N];
    void tarjan(int x){
        dfn[x]=low[x]=++ind;
        stk[++top]=x;ins[x]=1;
        for(int i=head[x];i!=-1;i=e[i].nxt){
            int y=e[i].to;
            if(!dfn[y]){
                tarjan(y);
                low[x]=min(low[x],low[y]);
            }
            else if(ins[y])
                low[x]=min(low[x],dfn[y]);
        }
        if(dfn[x]==low[x]){
            cnt++;
            int y;
            do{
                y=stk[top--];
                ins[y]=0;
                c[y]=cnt;
            }while(x!=y);
        }
    }

    scc的缩点:类似于e_DCC的缩点

    int main(){
        //...
        for(int x=1;x<=n;x++)
            for(int i=head[x];i!=-1;i=edge[i].nxt){
                int y=edge[i].to;
                if(c[x]==x[y])continue;
                add_c(c[x],c[y]);
            }
        //...
    } 
  • 相关阅读:
    35个Java代码优化的细节,你知道几个?
    Java如何优雅地实现接口数据校验
    Java中方法的重载详解(含系统配套视频)
    七大经典、常用排序算法的原理、Java 实现以及算法分析
    Java静态方法和实例方法
    Java静态变量和实例变量
    “反转链表”相关的题目
    【C++】将十进制数转换为十六进制数
    第四章:动态规划I
    【C++】数组中的第k个最小元素
  • 原文地址:https://www.cnblogs.com/zsben991126/p/10461993.html
Copyright © 2020-2023  润新知