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; }