POJ - 3177 Redundant Paths (边双连通缩点)

题意:在一张图中最少可以添加几条边,使其中任意两点间都有两条不重复的路径(路径中任意一条边都不同)。

分析:问题就是最少添加几条边,使其成为边双连通图。可以先将图中所有边双连通分量缩点,之后得到的就是一棵树。

那么问题又转化成为:在这棵树上添加几条边使其成为一个双连通分量。答案是缩点之后(leaf+1)/2,其中leaf是树的叶节点个数。

具体方法为,首先把两个最近公共祖先最远的两个叶节点之间连接一条边,这样可以把这两个点到祖先的路径上所有点收缩到一起,因为一个形成的环一定是双连通的。然后这样不断一对一地找完,次数正好是(leaf+1)/2次。
#include<iostream>
#include<stdio.h>
#include<stack>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
const int maxn =5e4+5;
struct Edge{
    int to,next;
}edges[maxn<<2];
bool instack[maxn];
int bccno[maxn],head[maxn],dfn[maxn],low[maxn],degree[maxn],clk,top,scc;
stack<int> S;

void init()
{
    clk = top = scc =0;
    memset(head,-1,sizeof(head));
    memset(dfn,0,sizeof(dfn));
    memset(bccno,0,sizeof(bccno));
    memset(instack,0,sizeof(instack));
    memset(degree,0,sizeof(degree));
}

void AddEdge(int u,int v)
{
    edges[top].to = v;
    edges[top].next =head[u];
    head[u] = top++;
}

void Tarjan(int u,int id)
{
    int v;
    low[u]=dfn[u]=++clk;
    S.push(u);
    instack[u]=true;
    for(int i=head[u];i!=-1;i=edges[i].next){
        v = edges[i].to;
        if(i==(id^1))   continue;
        if(!dfn[v]){
            Tarjan(v,i);
            low[u]=min(low[u],low[v]);
        }
        else if(instack[v])
            low[u]= min(low[u],dfn[v]);
    }
    if(dfn[u]==low[u]){     //找到一个双连通分量
        scc++;                  //从1开始
        int x;
        while(true){
            x =S.top();S.pop();
            bccno[x]=scc;               //确定分量编号
            instack[x]=false;
            if(x==u)    break;      //找到了自己就要停止标号
        }
    }
}

int main()
{
    int N,M,v,u,tmp;
    while(~scanf("%d%d",&N,&M)){
        init();
        for(int i=0;i<M;++i){
            scanf("%d%d",&u,&v);
            AddEdge(u,v);
            AddEdge(v,u);
        }
        Tarjan(1,-1);
        for(int i=1;i<=N;++i){
            for(int j =head[i];j!=-1;j=edges[j].next){
                v = edges[j].to;
                if(bccno[i]!=bccno[v]){     //根据分量编号缩点,计算度
                    degree[bccno[i]]++;     
                }
            }
        }
        int res=0;
        for(int i=1;i<=scc;++i){
            if(degree[i]==1)
                res++;
        }
        printf("%d\n",(res+1)/2);
    }
    return 0;
}

 

 

 

posted @ 2018-08-01 10:59  xiuwenL  阅读(138)  评论(0编辑  收藏  举报