Tarjan三大算法之双连通分量(双连通分量) (转载)

定义:
对于一个连通图,如果任意两点至少存在两条点不重复路径,则称这个图为点双连通的(简称双连通);如果任意两点至少存在两条边不重复路径,则称该图为边双连通的。点双连通图的定义等价于任意两条边都同在一个简单环中,而边双连通图的定义等价于任意一条边至少在一个简单环中。对一个无向图,点双连通的极大子图称为点双连通分量(简称双连通分量),边双连通的极大子图称为边双连通分量。这篇博客就是总结一下求解无向图点双连通分量与边双连通分量的方法。

算法
求解点双连通分量与边双连通分量其实和求解割点与桥密切相关。不同双连通分量最多只有一个公共点,即某一个割顶,任意一个割顶都是至少两个点双连通的公共点。不同边双连通分量没有公共点,而桥不在任何一个边双连通分量中,点双连通分量一定是一个边双连通分量。
下面首先介绍点双连通分量的Tarjan算法
在之前的博客中,我们已经知道如何求解割顶了,很容易可以发现,当我们找到割顶的时候,就已经完成了一次对某个极大点双连通子图的访问,那么我们如果在进行DFS的过程中将遍历过的点保存起来,是不是就可以得到点双连通分量了?为了实现算法,我们可以在求解割顶的过程中用一个栈保存遍历过的(注意不是点!因为不同的双连通分量存在公共点即割顶),之后每当找到一个点双连通分量,即子结点v与父节点u满足关系low[v]>=dfn[u],我们就将栈里的东西拿出来直到遇到当前边。
这里注意放入栈中的不是点,而是边,这是因为点双连通分量是存在重复点的,如果我们放入栈中的是点,那么对于某些点双连通分量,就会少掉一些点(这些点都是割顶)。
代码:

struct Edge{
    int u,v;
    Edge(int u=0,int v=0):u(u),v(v){}
}e[maxm];
int n,m,stamp,dfn[maxn],low[maxn],iscut[maxn],bccno[maxn];
int scnt,stack[maxm],bcc_cnt;
vector<int> vec[maxn],bcc[maxn];

void tarjan(int index,int fa)
{
    int child=0,tmp;
    dfn[index]=low[index]=++stamp;
    for(int i=0;i<vec[index].size();i++)
    {
        tmp=e[vec[index][i]].v;
        if(!dfn[tmp])
        {
            stack[++scnt]=vec[index][i],child++;
            tarjan(tmp,index);
            low[index]=min(low[index],low[tmp]);
            if(low[tmp]>=dfn[index])
            {
                iscut[index]=1;
                bcc[++bcc_cnt].clear();
                while(1)
                {
                    int num=stack[scnt--];
                    if(bccno[e[num].u]!=bcc_cnt)
                    {
                        bcc[bcc_cnt].push_back(e[num].u);
                        bccno[e[num].u]=bcc_cnt;
                    }
                    if(bccno[e[num].v]!=bcc_cnt)
                    {
                        bcc[bcc_cnt].push_back(e[num].v);
                        bccno[e[num].v]=bcc_cnt;
                    }
                    if(e[num].u==index && e[num].v==tmp)
                        break;
                }
            }
        }
        else if(dfn[tmp]<dfn[index] && tmp!=fa)
        {
            stack[++scnt]=vec[index][i];
            low[index]=min(low[index], dfn[tmp]);
        }
    }
    if(fa<0 && child==1)
        iscut[index]=0;
}

void find_bcc()
{
    // 割顶的bccno值无意义
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(iscut,0,sizeof(iscut));
    memset(bccno,0,sizeof(bccno));
    memset(bcc,0,sizeof(bcc));
    stamp=scnt=bcc_cnt=0;
    for(int i=1;i<=n;i++)
        if(!dfn[i])
            tarjan(i,-1);

这里需要十分注意的是,算法结束之后,每个结点会有一个编号,代表它属于哪一个点双连通分量,但是,割顶的编号是完全没有意义的!这个算法灵活使用了两个时间戳和栈,完成了点双连通分量的发现。
例题:UVALIVE 5135

之后介绍边双连通分量的求解算法:
边双连通分量的求解非常简单,因为边双连通分量之间没有公共边,而且桥不在任意一个边双连通分量中,所以算法十分简单,即先一次DFS找到所有桥,再一次DFS(排除了桥)找到边双连通分量。
PS:当然可以用一次DFS实现。
代码:

struct Edge{
    int u,v;
    Edge(int u=0,int v=0):u(u),v(v){}
}e[maxm];
int n,m,stamp,dfn[maxn],low[maxn],bccno[maxn],bcc_cnt;
vector<int> vec[maxn],bcc[maxn];
bool g[maxn][maxn],isbridge[maxm];

void tarjan(int index,int fa)
{
    int tmp;
    dfn[index]=low[index]=++stamp;
    for(int i=0;i<vec[index].size();i++)
    {
        tmp=e[vec[index][i]].v;
        if(!dfn[tmp])
        {
            tarjan(tmp,index);
            low[index]=min(low[index],low[tmp]);
            if(low[tmp]>dfn[index])
                isbridge[vec[index][i]]=isbridge[vec[index][i]^1]=1;
        }
        else if(dfn[tmp]<dfn[index] && tmp!=fa)
        {
            low[index]=min(low[index], dfn[tmp]);
        }
    }
}

void dfs(int index)
{
    dfn[index]=1;
    bccno[index]=bcc_cnt;
    for(int i=0;i<vec[index].size();i++)
    {
        int tmp=vec[index][i];
        if(isbridge[tmp])
            continue;
        if(!dfn[e[tmp].v])
        {
            dfs(e[tmp].v);
        }
    }
}

void find_ebcc(){
    bcc_cnt=stamp=0;
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(isbridge,0,sizeof(isbridge));
    memset(bccno,0,sizeof(bccno));
    memset(bcc,0,sizeof(bcc));
    for(int i=1;i<=n;i++)
        if(!dfn[i])
            tarjan(i, -1);
    memset(dfn,0,sizeof(dfn));
    for(int i=1;i<=n;i++)
    {
        if(!dfn[i])
        {
            bcc_cnt++;
            dfs(i);
        }
    }
}

POJ 3352

所谓双连通与强连通,最大的差别,也是最本质的差别就是前者适用于无向图中,而后者适用于有向图

时间: 2024-10-07 23:18:03

Tarjan三大算法之双连通分量(双连通分量) (转载)的相关文章

LCA(最近公共祖先)--tarjan离线算法 hdu 2586

HDU 2586 How far away ? Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 11320    Accepted Submission(s): 4119 Problem Description There are n houses in the village and some bidirectional roads c

模拟linux 双网卡双ip,实现multipath多路径连接存储

模拟linux 双网卡双ip,实现multipath多路径连接存储 系统(Centos6.5:oracle-1): eth0:192.168.21.23/24 eth1:172.16.112.17/24 存储Ip:  192.168.21.230 首先要配置iscsi存储,再其次一下操作,我这里配置了两个ISCIS存储 网卡路由配置 由于只能有一个网关,所以要添加静态路由实现双网卡通信 1.编辑网卡eth0,eth1 设置好配置文件,不添加默认网关 # vi /etc/sysconfig/net

双硬盘双系统,光驱位硬盘升级win10注意事项

双硬盘双系统,光驱位硬盘升级win10注意事项 这篇文章就是是接着上一篇文章的备忘,上次说到了双硬盘双系统: http://blog.csdn.net/aaa123524457/article/details/47058437 当然两个硬盘的系统都是win7,但是光驱位的硬盘是以前买笔记本的时候自带的,里面的win7是索尼自带的正版系统,我是打算用它来升级win10:因为工作需要,我把主硬盘位置装了一个更大的硬盘,工作用的是win7系统. 昨天收到win10的升级推送包后,就想都没想就更新了:更

linux 双网关双IP设置

server:CentOS5.8 ip:172.16.8.11 Gateway:172.16.8.1 ip:10.120.6.78 Gateway:10.120.6.1 网卡配置: eth0 point: [[email protected] ~]# cat /etc/sysconfig/network-scripts/ifcfg-eth0 # Intel Corporation 82576 Gigabit Network Connection DEVICE=eth0 BOOTPROTO=non

第8章双路由双网段网络互通实验(扫盲篇)

第8章双路由双网段网络互通实验(扫盲篇) 一  实验目的 学习如何给路由器接口配置IP.子网掩码 学习如何给路由器配置静态路由 学习如何在GNS3软件下给电脑主机配置IP 二  实验工具 GNS3软件   http://yunpan.cn/cwkDa4MWJwiiV  访问密码 9b1a 思科IOS    http://yunpan.cn/cjDjwg4CXMxpw  访问密码 9c14 三  实验步骤 如图所示在GNS3的模拟环境下,两台路由R1和R2,下面各接1台电脑,要求这2台电脑在不同网

芯飞凌调色温驱动SCR092/S4120//S4225 双驱双色温

芯飞凌调色温驱动  Q Q 2892715427 SCR092/S4120//S4225 双驱双色温 S4225LD 系列开关调色温控制芯片 特点 内置400V 开关管,无需外挂开关管 S4225LD系列芯片是开关调色温的专用芯 外围原件简单,无需对AC 输入检测 片,该芯片内置了400V的开关管,简化了外围电 多个电源同时使用时无逻辑紊乱的问题 路结构.该系列芯片采用了芯飞凌的专利技术,能 内置限压电路,保证可以更宽的工作范围 够既可以最大限度地简化外围的原件个数,又可以 兼容隔离和非隔离的应

基于双TMS320C6678+双XC6VSX315T的6U VPX高速数据处理平台

基于双TMS320C6678+双XC6VSX315T的6U VPX高速数据处理平台 一.板卡概述 板卡由我公司自主研发,基于VPX架构,主体芯片为两片 TI DSP TMS320C6678,两片Virtex-6 XC6VSX315T-ff1156 FPGA,1个RapidIO Switch.FPGA连接FMC子卡.FPGA片外挂接2簇32bit DDRIII SDRAM,最大容量支持2GB.每片FPGA还通过EMIF总线连接一片TMS320C6678型8核心DSP.所有信号处理FPGA与DSP均

windows2003服务器双线双IP双网卡设置方法

双线双ip很好,网通用户访问网通线路,电信用户访问电信线路.但很多人会选用导入静态路由表,这个办法看似完美,其实问题很多. 1.电信用户如果被解析到网通的ip上,服务器根据路由表会返回电信线路,但用户请求的是网通ip,所以用户什么也得不到,就好像你从一楼买了东西,商场却在二楼给你出货了,而且不告诉你要去二楼拿. 2.就算被正确解析了,但是如果是教育网用户,服务器会因为路由表里没有ip记录而不会返回任何东西. 3.ip不断的都在变化,也有新增的,要不停的维护路由表实在太累. 以上三条中的任意一条的

3-基于双TMS320C6678+双XC6VSX315T的6U VPX高速数据处理平台

基于双TMS320C6678+双XC6VSX315T的6U VPX高速数据处理平台 一.板卡概述 板卡由我公司自主研发,基于VPX架构,主体芯片为两片 TI DSP TMS320C6678,两片Virtex-6 XC6VSX315T-ff1156 FPGA,1个RapidIO Switch.FPGA连接FMC子卡.FPGA片外挂接2簇32bit DDRIII SDRAM,最大容量支持2GB.每片FPGA还通过EMIF总线连接一片TMS320C6678型8核心DSP.所有信号处理FPGA与DSP均