图论训练之八
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;
}