构造双连通图:一个有桥的连通图,如何把它通过加边变成边双连通图

一个有桥的连通图,如何把它通过加边变成边双连通图?方法为首先求出所有的桥,然后删除这些桥边,剩下的每个连通块都是一个双连通子图。把每个双连通子图收缩为一个顶点,再把桥边加回来,最后的这个图一定是一棵树,边连通度为1。

统计出树中度为1的节点的个数,即为叶节点的个数,记为leaf。则至少在树上添加(leaf+1)/2条边,就能使树达到边二连通,所以至少添加的边数就是(leaf+1)/2。具体方法为,首先把两个最近公共祖先最远的两个叶节点之间连接一条边,这样可以把这两个点到祖先的路径上所有点收缩到一起,因为一个形成的环一定是双连通的。然后再找两个最近公共祖先最远的两个叶节点,这样一对一对找完,恰好是(leaf+1)/2次,把所有点收缩到了一起。

题目: http://poj.org/problem?id=3352

题意:给你一个连通的无向图,现在问你最少在该图中添加几条边,能使得该图变成边双连通图?

我没有搞懂为什么至少在树上添加(leaf+1)/2条边,就能使树达到边二连通,暂且记住吧。

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <stack>
#define N 10010
using namespace std;
struct node
{
    int x,y,w,next,flag;
} eg[2*N];
stack<int>q;
int tt,head[N],dfn[N],low[N],ti,n,m,be[N],bridge[N][2],tp;
int cnt,de[N];
void init()
{
    tt=0;
    ti=0;
    tp=0;
    cnt=0;
    memset(head,-1,sizeof(head));
    memset(dfn,0,sizeof(dfn));
    memset(be,0,sizeof(be));
    memset(de,0,sizeof(de));
    memset(bridge,0,sizeof(bridge));
    while(!q.empty()) q.pop();
}
void add(int xx,int yy)
{
    eg[tt].x=xx;
    eg[tt].y=yy;
    eg[tt].next=head[xx];
    head[xx]=tt++;
}
void tarjan(int u,int fa)
{
    dfn[u]=low[u]=++ti;
    q.push(u);
    for(int i=head[u]; i!=-1; i=eg[i].next)
    {
        int v=eg[i].y;
        if(v==fa) continue;
        if(!dfn[v])
        {
            tarjan(v,u);
            low[u]=min(low[u],low[v]);
            if(low[v]>dfn[u])//边(u,v)为桥,可以统计一个边连通分支
            {
                bridge[tp][0]=u;
                bridge[tp++][1]=v;
                ++cnt;
                int w;
                do
                {
                    w=q.top();
                    q.pop();
                    be[w]=cnt;
                }
                while(w!=v);//注意点u并没有出栈,因为点u属于另一个边连通分量
            }
        }
        else
        {
            low[u]=min(dfn[v],low[u]);
        }
    }
}
int main()
{
    int xx,yy;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        init();
        for(int i=1; i<=m; i++)
        {
            scanf("%d%d",&xx,&yy);
            add(xx,yy);
            add(yy,xx);
        }
        tarjan(1,-1);
        if(!q.empty())
        {
            ++cnt;
            int w;
            do
            {
                w=q.top();
                q.pop();
                be[w]=cnt;
            }
            while(w!=1);
        }
        for(int i=0; i<tp; i++)
        {
            int u=bridge[i][0];
            int v=bridge[i][1];
            de[be[u]]++;
            de[be[v]]++; //统计缩点后的的度
        }
        /*for(int i=0;i<tp;i++)
        {
            printf("bridge==%d %d\n",bridge[i][0],bridge[i][1]);
        }
        printf("cnt==%d\n",cnt);*/
        int leaf=0;
        for(int i=1; i<=cnt; i++)
        {
            if(de[i]==1)
                leaf++;
        }
        printf("%d\n",(leaf+1)/2);
    }
    return 0;
}

第一次交的,需要用数组f[N]防止跨边,我感觉实际上这样是没用的,但大家代码都这么写我就贴一下吧,无向图是没有跨边的。

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <stack>
#define N 10010
using namespace std;
struct node
{
    int x,y,w,next,flag;
} eg[2*N];
stack<int>q;
int tt,head[N],dfn[N],low[N],ti,n,m,be[N],bridge[N][2],tp;
int cnt,de[N],f[N];
void init()
{
    tt=0;
    ti=0;
    tp=0;
    cnt=0;
    memset(head,-1,sizeof(head));
    memset(dfn,0,sizeof(dfn));
    memset(be,0,sizeof(be));
    memset(de,0,sizeof(de));
    memset(bridge,0,sizeof(bridge));
    memset(f,0,sizeof(f));
    while(!q.empty()) q.pop();
}
void add(int xx,int yy)
{
    eg[tt].x=xx;
    eg[tt].y=yy;
    eg[tt].next=head[xx];
    head[xx]=tt++;
}
void tarjan(int u,int fa)
{
    dfn[u]=low[u]=++ti;
    q.push(u);
    f[u]=1;
    for(int i=head[u]; i!=-1; i=eg[i].next)
    {
        int v=eg[i].y;
        if(v==fa) continue;
        if(!dfn[v])
        {
            tarjan(v,u);
            low[u]=min(low[u],low[v]);
            if(low[v]>dfn[u])//边(u,v)为桥,可以统计一个边连通分支
            {
                bridge[tp][0]=u;
                bridge[tp++][1]=v;
                ++cnt;
                int w;
                do
                {
                    w=q.top();
                    q.pop();
                    f[w]=0;
                    be[w]=cnt;
                }
                while(w!=v);//注意点u并没有出栈,因为点u属于另一个边连通分量
            }
        }
        else if(f[v])
        {
            low[u]=min(dfn[v],low[u]);
        }
    }
}
int main()
{
    int xx,yy;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        init();
        for(int i=1; i<=m; i++)
        {
            scanf("%d%d",&xx,&yy);
            add(xx,yy);
            add(yy,xx);
        }
        tarjan(1,-1);
        if(!q.empty())
        {
            ++cnt;
            int w;
            do
            {
                w=q.top();
                q.pop();
                be[w]=cnt;
            }
            while(w!=1);
        }
        for(int i=0;i<tp;i++)
        {
            int u=bridge[i][0];
            int v=bridge[i][1];
            de[be[u]]++;
            de[be[v]]++; //统计缩点后的的度
        }
        /*for(int i=0;i<tp;i++)
        {
            printf("bridge==%d %d\n",bridge[i][0],bridge[i][1]);
        }
        printf("cnt==%d\n",cnt);*/
        int leaf=0;
        for(int i=1;i<=cnt;i++)
        {
            if(de[i]==1)
            leaf++;
        }
        printf("%d\n",(leaf+1)/2);
    }
    return 0;
}
View Code

相关文章:

  • 2022-01-26
  • 2022-01-21
  • 2021-11-28
  • 2021-09-06
  • 2022-12-23
  • 2022-12-23
  • 2021-10-14
猜你喜欢
  • 2022-12-23
  • 2022-12-23
  • 2021-08-04
  • 2022-12-23
  • 2021-08-25
  • 2022-12-23
  • 2021-12-22
相关资源
相似解决方案