POJ3177 Redundant Paths【双连通分量】

题意:

有F个牧场,1<=F<=5000,现在一个牧群经常需要从一个牧场迁移到另一个牧场。奶牛们已经厌烦老是走同一条路,所以有必要再新修几条路,这样它们从一个牧场迁移到另一个牧场时总是可以选择至少两条独立的路。现在F个牧场的任何两个牧场之间已经至少有一条路了,奶牛们需要至少有两条。给定现有的R条直接连接两个牧场的路,F-1<=R<=10000,计算至少需要新修多少条直接连接两个牧场的路,使得任何两个牧场之间至少有两条独立的路。两条独立的路是指没有公共边的路,但可以经过同一个中间顶点。

思路:

给定一个无向连通图,判断至少需要加多少条边,使得任意两点之间至少有两条相互‘边独立’的道路,也就是说,至少加多少条边,使得这个图成为一个边双连通图。首先已经有了一个连通图,那么将所有的双连通块看成一个点,这样可以得到一颗树,因为它们当中没有环,而又是一个连通的图,所以可以肯定的是,这样缩点之后可以得到一颗树,这样的树至少需要加多少条边就能构成一个双连通图呢,只需要将叶子节点连起来即可,所以最少要连的边=(叶子节点数+1)/2。而求边连通分量,用tarjan就可以求出哪个点属于哪个双连通块中,然后再计算度为1的节点就可以了。因为是一个无向图,所以是度为1的点为叶子节点,而不是度为0。

代码:

Tarajn:

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
#define maxn 5005

int n,m,low[maxn],dfn[maxn],sum,cnt[maxn];
bool map[maxn][maxn],vis[maxn];

void init()
{
    memset(vis,0,sizeof(vis));
    memset(low,0,sizeof(low));
    memset(dfn,0,sizeof(dfn));
    sum=0;
}

void dfs(int x,int pre){
    int i;
    vis[x]=1;
    low[x]=dfn[x]=++sum;
    for(i=1;i<=n;i++){
        if(map[x][i]){
            if(!vis[i]){
                dfs(i,x);
                low[x]=min(low[x],low[i]);
            }
            if(vis[i]==1 && i!=pre){
                low[x]=min(low[x],dfn[i]);
            }
        }
    }
}

int main()
{
    int a,b,i,j;
    while(cin>>n>>m)
    {
        memset(map,0,sizeof(map));
        while(m--)
        {
            cin>>a>>b;
            map[a][b]=map[b][a]=1;
        }
        init();
        dfs(1,1);
        memset(cnt,0,sizeof(cnt));
        for(i=1;i<=n;i++)   //计算每个点的度
        {
            for(j=1;j<=n;j++)
                if(map[i][j])
                {
                    if(low[i]!=low[j])
                    {
                        cnt[low[j]]++;
                    }
                }
        }
        int ans=0;                //计算度为1的点的个数
        for(i=1;i<=n;i++)
        {
            if(cnt[i]==1) ans++;
        }
        cout<< (ans+1)/2 <<endl;;
    }
    return 0;
}
原文地址:https://www.cnblogs.com/darklights/p/7649700.html