双连通分量小结

割点(割顶):割点即割顶,去掉割点后,原图的连通分量变多;
割边(桥):割边即桥,去掉割边后,原图的连通分量变多;

点双连通分量:任意两点之间存在至少两条顶点不同的路径/去掉任意一个点后,图的连通性不变
边双连通通分量:任意两点之间存在至少两条边不同的路径/去掉任意一条边后,图的连通性不变

点双连通和边双连通的区别与联系:

  1. 两者都是基于无向图而言的
  2. 点双连通分量一定是边双连通分量(除了两点一线,两点一线是点双,但不是边双),反之不一定
  3. 点双连通分量之间可以有公共点,但也只能有一个(该点是割点);因此在求点双连通的时候,可以找割点,但是入栈的要是边,因为一个点可能属于多个点双连通分量,若存的是点,那割点出栈后还要入栈。如果存的是边,则不用,但出边的时候要判断边的两个端点是否和之间出去的有重复
  4. 边双连通分量之间不能有公共点或公共边,连接两个边双连通分量的是割边;因此边双连通就方便了,一个点只能属于一个边双连通分量

二分图:下面针对的是无向图
判定:先对任意一个顶点染色,然后找它相邻点
    *若为染色,则染色(递归此点)
    *已染色,且颜色和当前结点相同(失败,该图不是二分图)
    *已染色,且颜色和当前结点不同,跳过
    
一些定理:
    *如果一个双连通分量内的某些顶点在一个奇圈中(即双连通分量含有奇圈),那么这个双连通分量的其他顶点也在某个奇圈中
    证明:假设有一个奇圈,因为是点双,没有割点,必然有紧挨着的圈,设公共点有k个(至少有两个公共点),假设这个圈是偶圈,则这个偶圈必然能和原来的奇圈组成新的奇圈(新的圈=(奇圈-k)+(偶圈-k)= 奇数);这里k是两个圈的公共边上的点数(不包含端点);如果这个圈是奇圈,则也得证
    *如果一个双连通分量含有奇圈,则它一定不是二分图;反之,如果一个图是二分图,它一定没有奇圈

   对于定理一,如下图,若有一个奇圈(1,2,3,4,5),和它相邻的有一个偶圈(4,5,3,6);则它们的k是1(即公共边<4,5> <5,3>这两条边上的点5,端点3,4不算);新的圈=(5-1)+(4-1)-2 =7,即(1,2,3,6,4).组成一个新的奇圈;即若一个双连通分量内含有一个奇圈,则其他点必定在某个奇圈中(一个点可能属于多个奇圈)

//代码:
/**
求解方法:不断把某个点的树边和方向边入栈,更新当前点的Low,如果某个点是割点,就把栈里存的边弹出
注意:子节点的low可以用来更新父节点的low,而方向边只能通过dfn来更新当前点的low。不能拿方向边的点(祖先结点)的Low来更新当前low,因为祖先结点的low不一定根当前结点有关系
*/
void tarjan(int u,int fa)
{
    int child = 0;
    low[u] = dfn[u] = ++dfn_cnt;
    for(int i = 0; i < G[u].size(); i++)
    {
        int v = G[u][i];
        pii p = MP(u,v);	//取得当前这条边
        if(!dfn[v])			//如果还没被访问,说明是一条树边
        {
            S.push(p);	//必须在递归前推入栈中
            child++;
            tarjan(v,u);
            low[u] = min(low[u],low[v]);
            if(low[v] >= dfn[u]) ///***当前子节点会不会连向u的祖先结点,如果不会,则u去掉后,v子分支
                				//肯定就断开了。这个判断要在tarjan完这个子节点就判断,因为一个
                				//结点u可以连着多个点双,不会相互影响,相反,如果等遍历完所有子节点
                				//才判断就不行了。这里不能换成low[v] >= low[u],应该是和u的dfs序
                				//比较,看是不是会连到u的祖先结点上
            {
                iscut[u] = true;
                bcc_cnt++;
                bcc[bcc_cnt].clear();
                for(;;){
                    pii tmp = S.top(); S.pop();
                    //判重
                    if(bccno[tmp.X] != bcc_cnt) bcc[bcc_cnt].PB(tmp.X),bccno[tmp.X] = bcc_cnt;
                    if(bccno[tmp.Y] != bcc_cnt) bcc[bcc_cnt].PB(tmp.Y),bccno[tmp.Y] = bcc_cnt;
                    if(tmp.X == u && tmp.Y == v) break;
                }
            }
        } else if(dfn[v] < dfn[u] && v != fa)//该点已被访问过,看看是不是在u的上边,若是,则是一条
            								//回边,入栈
            								//这里v!=fa也很重要,因为双连通是针对无向图,我们建图的时候是有正反向边的。
        {
            S.push(p);
            low[u] = min(low[u],dfn[v]);//这里不能用min(low[u],low[v]),v是u的祖先结点,祖先结点
            						//的low值对其孩子结点来说是不适用的
        }
    }
    if(fa < 0 && child > 1) iscut[u] = true;
}

对于下述情况,经过dfn[a1] < dfn[u],注意这里比较的是结点的先后次序,不是Low值;主要是看该结点是不是u的回向边,所以是根据dfn来比较的;然后low[u] = min(low[u],dfn[v]);这里不能用low[v],不然u的箭头就指向a0了,这样整条线构成点双,但实际情况并不是

 

边双连通分量

    对于边双连通,第一遍dfs找出所有的桥,第二遍dfs遍历整个图,遇到桥或已经遍历过的结点则停止。即可找出每个边双连通分量。此种做法可以判重边和环,如果1--2(1,2连边)则有两个边双,如果是1--2,2--1则有一个边双。具体过程可以参考代码模拟一遍即可。

void tarjan(int u,int fa)
{
    low[u] = dfn[u] = ++dfn_cnt;
    for(int i = head[u]; ~i; i = edge[i].next)
    {
       int v = edge[i].v;
       //dbg(u,v,"*");
       if(!dfn[v])
       {
           tarjan(v,u);
           low[u] = min(low[u],low[v]);
           if(low[v] > dfn[u])
            {
                isbridge[i] = isbridge[i^1] = true;
                //dbg("****");
            }
       }else if(dfn[v] < dfn[u] && v != fa)
            low[u] = min(low[u],dfn[v]);
    }
}

void dfs_bcc(int u,int id)
{
    bccno[u] = id;
    for(int i = head[u]; ~i; i = edge[i].next)
    {
        if(!isbridge[i])
        {
            int v = edge[i].v;
            if(!bccno[v]) dfs_bcc(v,id);
        }
    }
}

注意点:

   双连通(包括边和点)分量都是针对无向图,因此建边的时候要建立正反边。并且dfs的时候记得判断某点的子边是不是它的父节点。任意两个边双连通分量之间都没有公共边,公共点(因此可以缩点??),这点和强连通分量一样。缩点建图后的图一定没有环

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值