题意:加上最少的边,使得改造后的图中去掉任意一条边后图依然连通。(图是非简单图,需要判重)
题解:先找出边双连通分量,然后缩点,的到一棵树。需要加的最少边=(leaves+1)/2
我们可以发现low[4]=3,low[7]=4 但是我们知道<4,7>这条边并不是割边.所以low[u]!=low[v]是割边的必要不充分条件.
#include <iostream>
using namespace std;
#define N 10010
#define min(a,b) (a<b?a:b)
int f, r;
int size, id, top, scc;
int instack[N], stack[N];
int dfn[N], low[N], block[N];
int head[N], degree[N];
struct { int v, next; } edge[N*2];
void add ( int u, int v )
{
size++;
edge[size].v = v;
edge[size].next = head[u];
head[u] = size;
}
bool judge ( int u, int father ) // 判断是否有重边
{
int repeat = 0;
for ( int i = head[u]; i; i = edge[i].next )
if ( edge[i].v == father ) repeat++;
if ( repeat >= 2 ) return true;
return false;
}
void Tarjan ( int u, int father )
{
int v, t;
stack[++top] = u;
instack[u] = 1;
dfn[u] = low[u] = ++id;
bool repeat = judge ( u, father );
for ( int i = head[u]; i; i = edge[i].next )
{
v = edge[i].v;
if ( v == father && ! repeat ) continue;
if ( ! dfn[v] )
{
Tarjan ( v, u );
low[u] = min ( low[u], low[v] );
if ( low[v] > dfn[u] )
{
scc++;
do {
t = stack[top--];
instack[t] = 0;
block[t] = scc;
} while ( t != v );
}
}
else if ( instack[v] )
low[u] = min ( low[u], dfn[v] );
}
}
int shrink ()
{
int u, v, i, leaves = 0;
for ( u = 1; u <= f; u++ )
{
for ( i = head[u]; i; i = edge[i].next )
{
v = edge[i].v;
if ( block[u] != block[v] )
{
degree[block[u]]++;
degree[block[v]]++;
}
}
}
for ( i = 0; i <= scc; i++ )
if ( degree[i] == 2 ) leaves++;
return (leaves+1)/2;
}
void initial ()
{
size = scc = top = id = 0;
for ( int i = 0; i <= f; i++ )
{
dfn[i] = head[i] = instack[i] = 0;
low[i] = degree[i] = block[i] = 0;
}
}
int main()
{
int u, v;
while ( scanf("%d%d",&f,&r) != EOF )
{
initial();
while ( r-- )
{
scanf("%d%d",&u,&v);
add ( u, v );
add ( v, u );
}
for ( int i = 1; i <= f; i++ )
if ( ! dfn[i] ) Tarjan ( i, f+1 );
printf("%d\n",shrink());
}
return 0;
}