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

流图:存在点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]);
        }
    //...
} 
原文地址:https://www.cnblogs.com/zsben991126/p/10461993.html