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

流图:存在点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 cnt;
vector<int>scc[maxn];
void tarjan(int x){
    dfn[x]=low[x]=++ind;
    stack[++top]=x,ins[x]=1;
    for(int i=head[u];i!=-1;i=edge[i].nxt){
        int y=edge[i].to;
        if(!dfn[y]){//树枝边 
            tarjan(y);
            low[x]=min(low[x],low[y]);
        }
        else if(ins[y])//后向边
            low[x]=min(low[x],low[y]);
    }
    if(dfn[x]==low[x]){
        cnt++;
        do{//染色,退栈 
            int y=stack[top--];
            ins[y]=0;
            c[y]=cnt,scc[cnt].push_back(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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值