Tarjan算法——边双和点双

本文详细介绍了边双连通分量与点双连通分量的概念及求解方法。通过Tarjan算法识别割边和割点,并进行连通分量的划分与缩点操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

边双连通分量

边双连通图:如果一个无向连通图中,没有割边,那么这个无向连通图就是一个边双连通图。

一个无向图的极大边双连通子图就是它的其中一个边双连通分量。
我们要解释下这里“极大”的概念:如果一个连通子图G1G1是边双,那么不存在一个原图的子图G2G2既满足G1G2G1∈G2又满足G2G2是边双

边双的“极大”不是指整个图范围内的最大,而是所有把某一个边双作为子图的所有连通子图的范围内而谈的。

求法

tarjan求出所有割边,然后把割边去掉,就是一个个边双连通分量了。
然后只要去掉割边进行dfs染色就行了。

int low[maxn],dfn[maxn],cnt=0;
    bool bridge[maxm<<1];
    void tarjan(int x,int in_edge){
        dfn[x]=low[x]=++cnt;
        for(int i=head[x];i+1;i=a[i].next){
            int y=a[i].y;
            if(!dfn[y]){
                tarjan(y,i);
                low[x]=min(low[x],low[y]);
                if(low[y]>low[x])bridge[i]=bridge[i^1]=true;
            }
            else if(i!=(in_edge^1))
                low[x]=min(low[x],dfn[y]);
        }
    }
    int block[maxn],dcc=0;
    void dfs(int x,int color){
        block[x]=color;
        for(int i=head[x];i+1;i=a[i].next){
            int y=a[i].y;
            if(bridge[i])continue;
            if(!block[y])dfs(y,color);
        }
    }
    void get_edccs(){
        for(int i=1;i<=n;i++)
            if(!block[i])
                dfs(i,++dcc);
    }

边双缩点

只要把所有的边双作为点,然后把割边保留下来建新树就行了。

点双连通分量

参照边双的定义,我们可以定义出点双:
在一个无向连通图中,如果没有割点,那么它是点双连通图。
如果只有两个点,那么也是点双。
一个无向图中的极大点双连通子图就是点双连通分量。
这里的“极大”同上。
我们可以发现,一个割点可以属于多个点双。但是一条割边不属于任何边双。

求法(如果不理解可以手推一组小数据)

开一个栈,tarjan递归访问到某个点的时候入栈,然后每次经过一条边(x,y)(x,y)而且x满足low[y]>=dfn[x]low[y]>=dfn[x]的时候不管x是不是割点,都把栈里的元素一一弹出,直到把y弹出,所有弹出的点,再加上x,构成一个点双。

void tarjan(int x,int in_edge){
        low[x]=dfn[x]=++cnt;sta[++t]=x;
        for(int i=head[x];i+1;i=a[i].next){
            int y=a[i].y;
            if(!dfn[y]){
                tarjan(y,i);
                low[x]=min(low[x],low[y]);
                if(low[y]>=dfn[x]){
                    int u;
                    dcc++;size=0;
                    vdcc.clear();
                    do{
                        u=sta[t--];
                        block[u]=dcc;
                        size++;
                        vdcc.push_back(u);
                    }while(u!=y);
                    block[x]=dcc;
                    vdcc.push_back(x);
                    size++;
                    work();
                }
            }
            else low[x]=min(low[x],dfn[y]);
        }
    }

点双缩点

每一个割点建点,每一个点双建点,然后根据从属关系连边。
这里我们画个图吧
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值