poj3177 Redundant Paths 边双连通分量

给一个无向图,问至少加入多少条边能够使图变成双连通图(随意两点之间至少有两条不同的路(边不同))。

图中的双连通分量不用管,所以缩点之后建新的无向无环图。

这样,题目问题等效于,把新图中度数为1的点相互连到图里面形成环

如果这种点有sum个,那么至少须要加入(sum+1)/2 条边。

下面,基本上就是求边双连通分量模板。


#include <iostream>
#include <cstring>
#include <string>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
#define inf 0x3f3f3f3f
#define eps 1e-6
#define ll __int64
using namespace std;
#define N 5010
#define M 10010

struct node
{
    int v,next,vis;
},e[M];
int h,head[N];
int belong[N],vis[N],dfn[N],low[N],brig[N][2],nbrig,col,cnt,top,in[N],sta[N];
int n,m;

void addedge(int a,int b)
{
    e[h].v=b;
    e[h].vis=0;
    e[h].next=head[a];
    head[a]=h++;
}

void tarjan(int u)
{
    int v;
    vis[u]=1;
    dfn[u]=low[u]=++cnt;
    sta[top++]=u;
    for(int i=head[u];i!=-1;i=e[i].next)
    {
        if(e[i].vis) continue;
        e[i].vis=e[i^1].vis=1;
        v=e[i].v;
        if(vis[v]==1)
            low[u]=min(low[u],dfn[v]);
        if(!vis[v])
        {
            tarjan(v);
            low[u]=min(low[u],low[v]);
            if(low[v]>dfn[u])
            {
                brig[nbrig][0]=u;
                brig[nbrig++][1]=v;
            }
        }
    }
    if(low[u]==dfn[u])
    {
        ++col;
        do
        {
            v=sta[--top];
            vis[v]=0;
            belong[v]=col;
        }while(u!=v);
    }
}

void init()
{
    memset(head,-1,sizeof head);
    memset(vis,0,sizeof vis);
    memset(belong,-1,sizeof belong);
    h=h1=cnt=col=nbrig=top=0;
}

int main()
{
    int i,j,k,a,b;
    while(~scanf("%d%d",&n,&m))
    {
        init();
        for(i=0;i<m;i++)
        {
            scanf("%d%d",&a,&b);
            addedge(a,b);
            addedge(b,a);
        }
        tarjan(1);
        memset(in,0,sizeof in);//记度数//无向图
        for(i=0;i<nbrig;i++)//缩点后 树的边是原图里的桥组成
        {
            a=brig[i][0];
            b=brig[i][1];
            if(belong[a]!=belong[b])
                in[belong[a]]++,in[belong[b]]++;
        }
        int sum=0;
        for(i=1;i<=col;i++)
            if(in[i]==1) sum++;
        printf("%d
",(sum+1)/2);
    }
    return 0;
}


以下这个模板太龊。。

#include <iostream>
#include <cstring>
#include <string>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
#define inf 0x3f3f3f3f
#define eps 1e-6
#define ll __int64
using namespace std;
#define N 1010
#define M 20010

struct node
{
    int v;//下个顶点
    node *next;//下个边结点
};
int n,m,r[N],d[N];//d表示缩点后各顶点的度数
node mem[M];int memp;//存储边结点
node *e[N];
int necc;//原图中边双连通分量的个数
int belong[N];
int low[N],dfn[N];
int vis[N];
int brig[M][2],nbrig;

void addedge(int i,int j)
{
    node *p=&mem[memp++];
    p->v=j;
    p->next=e[i];
    e[i]=p;
}

int root(int a)
{
    if(a!=r[a])
        r[a]=root(r[a]);
    return r[a];
}

void merge(int a,int b)
{
    int ra=root(a);
    int rb=root(b);
    if(ra!=rb)
        r[ra]=rb;
}

void dfs(int i,int father,int dth)
{
    int j,tofa=0;
    node *p;
    vis[i]=1;
    low[i]=dfn[i]=dth;
    for(p=e[i];p!=NULL;p=p->next)
    {
        j=p->v;
        if(vis[j]&&(j!=father||tofa))
            low[i]=min(low[i],dfn[j]);
        if(!vis[j])
        {
            dfs(j,i,dth+1);
            low[i]=min(low[i],low[j]);
            if(low[j]<=dfn[i]) merge(i,j);//i,j在同一个双联通分量
            if(low[j]>dfn[i]) //边i,j是桥
            {
                brig[nbrig][0]=i;
                brig[nbrig++][1]=j;
            }
        }
        if(j==father) tofa=1;
    }
    vis[i]=2;
}

int doubleconne()
{
    int i,k,ncon=nbrig=0;
    dfs(0,-1,1);
    for(i=0;i<n;i++)
    {
        k=root(i);
        if(belong[k]==-1) belong[k]=ncon++;
        belong[i]=belong[k];
    }
    return ncon;
}

void init()
{
    memp=nbrig=0;
    memset(e,0,sizeof e);
    memset(d,0,sizeof d);
    for(int i=0;i<=n;i++)
        r[i]=i;
    memset(vis,0,sizeof vis);
    memset(belong,-1,sizeof belong);
}

int main()
{
    int i,j,k,a,b;
    while(~scanf("%d%d",&n,&m))
    {
        init();
        for(i=0;i<m;i++)
        {
            scanf("%d%d",&a,&b);
            addedge(a-1,b-1);
            addedge(b-1,a-1);
        }
        necc=doubleconne();
        for(k=0;k<nbrig;k++)
        {
            i=brig[k][0];
            j=brig[k][1];
            d[belong[i]]++;
            d[belong[j]]++;
        }
        int cnt=0;//收缩后叶子结点的个数
        for(i=0;i<necc;i++)
            if(d[i]==1) cnt++;
        printf("%d
",(cnt+1)/2);
    }
    return 0;
}



原文地址:https://www.cnblogs.com/gcczhongduan/p/3998041.html