图论训练之八

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

题目描述

给一个连通图,问最少加几条边可以得到边双连通图。

分析:

模板题,复习一下

tarjan双向边缩点,再找叶子结点(即出度为1的点)

其实本题可以不用缩点,只用维护low数组就行,

只要low数组的值相等,就是属于同一连通块

为什么要找叶子结点呢?

结论:一个无向图通过加边得到边双联通图至少要(叶子结点数目+1)/2

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int MAXN = 5010;
vector<int>edge[MAXN];
int n, m;
int low[MAXN], dfn[MAXN], tmpdfn, in[MAXN];
int min( int a, int b ){return a<b ?a:b;}
void init()
{
    int i;
    for( i = 1; i <= n ; i++ )edge[i].clear();
    memset(in,0,sizeof(int)*(n+1));
    memset(dfn,0,sizeof(dfn));
    tmpdfn=0;
}
void tarjan( int u, int p )
{
    int i, v, size;
    low[u] = dfn[u] = ++tmpdfn;
    size = edge[u].size();
    for( i = 0; i < size; i++ )
    {
        v = edge[u][i];
        if( !dfn[v] )
            tarjan( v, u ), low[u] = min( low[u], low[v] );
        else if( v != p )low[u] = min( low[u], dfn[v] );
    }
}
int count()
{
    int cnt, u, v, i, size;
    cnt = 0;
    for( u = 1; u <= n; u++ )
    {
        size = edge[u].size();
        for( i = 0; i < size; i++ )
        {                            // low 相同的节点,归属于同一双连通分量
            v = edge[u][i];
            if( low[v] != low[u] )in[low[u]]++;
        }
    }
    for( u = 1; u <= n; u++ )if( in[u] == 1 )
        cnt++;
    return cnt;
}
int main()
{
    int i, j, u, v, flag, size;
    scanf( "%d%d", &n, &m );
    init();
    for( i = 0; i < m ; i++ )
    {
        scanf( "%d%d", &u, &v );
        flag = 0;    size = edge[u].size();
        for( j = 0; j < size; j++ )
            if( edge[u][j] == v ){
                flag = 1; break;
            }
        if( flag )continue;
        edge[u].push_back( v );
        edge[v].push_back( u );
    }
    tarjan( 1, -1 );
    printf( "%d\n", ( count() + 1 ) >> 1 );
    return 0;
}
posted @ 2019-10-09 19:17  wzx_believer  阅读(90)  评论(0编辑  收藏  举报