POJ3177求边双联通分量

参考博客:
jumping_frog
poj 3177 Redundant Paths(边双连通分量+缩点)

链接:http://poj.org/problem?id=3177

题意:有n个牧场,Bessie 要从一个牧场到另一个牧场,要求至少要有2条独立的路可以走。现已有m条路,求至少要新建多少条路,使得任何两个牧场之间至少有两条独立的路。两条独立的路是指:没有公共边的路,但可以经过同一个中间顶点。

分析:在同一个边双连通分量中,任意两点都有至少两条独立路可达,所以同一个边双连通分量里的所有点可以看做同一个点。

缩点后,新图是一棵树,树的边就是原无向图的桥。

现在问题转化为:在树中至少添加多少条边能使图变为双连通图。

结论:添加边数=(树中度为1的节点数+1)/2

具体方法为,首先把两个最近公共祖先最远的两个叶节点之间连接一条边,这样可以把这两个点到祖先的路径上所有点收缩到一起,因为一个形成的环一定是双连通的。然后再找两个最近公共祖先最远的两个叶节点,这样一对一对找完,恰好是(leaf+1)/2次,把所有点收缩到了一起。

其实求边双连通分量和求强连通分量差不多,每次访问点的时候将其入栈,当low[u]==dfn[u]时就说明找到了一个连通的块,则栈内的所有点都属于同一个边双连通分量,因为无向图要见反向边,所以在求边双连通分量的时候,遇到反向边跳过就行了。

网上有一种错误的做法是:因为每一个双连通分量内的点low[]值都是相同的,则dfs()时,对于一条边(u,v),只需low[u]=min(low[u],low[v]),这样就不用缩点,最后求度数的时候,再对于每条边(u,v)判断low[u]是否等于low[v],若low[u]!=low[v],则不是同一个边双连通分量,度数+1即可…..

咋看之下是正确的,但是这种做法只是考虑了每一个强连通分量重只有一个环的情况,如果有多个环,则会出错。

比如这组数据:
11 14
1 7
7 11
7 8
8 10
8 9
9 11
9 10
1 2
2 6
2 5
5 3
5 4
4 6
4 3
画出来图如下(括号里是dfn值):

由于一个边双联通分量里出现了两个环所以最后点10的low值是4而不是2,所以该方法有错。

代码:

#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <vector>

using namespace std;

const int maxn = 5005;
int map[1000][1000]; 
bool instack[maxn];
int stack[maxn],low[maxn],dfn[maxn],color[maxn],degree[maxn];
int n, m, sum, top, cnt;

vector <int> G[maxn];

void tarjan(int u,int father)
{
    low[u] = dfn[u] = ++cnt;
    stack[++top] = u;
    instack[u] = true;
    int v;
    for (int i = 0; i < G[u].size(); i++)
    {
        v = G[u][i];
        if (!dfn[v])
        {
            tarjan(v,u);
            low[u] = min(low[u],low[v]);
        }
        else if (v != father && instack[v])
            low[u] = min(low[u],dfn[v]);
    }
    if (dfn[u] == low[u])//同一边双联通量进行缩点
    {
        sum++;
        do
        {
            v = stack[top--];
            instack[v] = false;
            color[v] = sum;
        }while (v != u);
    }
}

void init()
{
    memset(map,0,sizeof(map));
    memset(dfn,0,sizeof(dfn));
    memset(instack,0,sizeof(instack));
    memset(degree,0,sizeof(degree));
    for (int i = 0; i < maxn; i++)
        G[i].clear();
    cnt = sum = top = 0;
}

int main()
{
    int u, v;
    while (scanf("%d%d",&n,&m) != EOF)
    {
        init();
        for (int i = 0; i < m; i++)
        {
            scanf("%d%d",&u,&v);
            if (map[u][v]) continue;
            G[u].push_back(v);
            G[v].push_back(u);
            map[u][v] = map[v][u] = 1;//这题要去重边,找bug找半天,我***
        }
        for (int i = 1; i <= n; i++)
            if (!dfn[i])
                tarjan(i,-1);
        for (int i = 1; i <= n; i++)
        {
            for (int j = 0; j < G[i].size(); j++)
            {
                v = G[i][j];
                if (color[i] != color[v])
                    degree[color[v]]++;
            }
        }
        int ans = 0;
        for (int i = 1; i <= sum; i++)
            if (degree[i] == 1)
                ans++;
        printf("%d\n",(ans+1)/2);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/bpdwn2017/article/details/80172164
今日推荐