题意:
求加入一条边(双向)后,图中桥最小的数量。
知识点:
tarjan算法实现无向图缩点(变成树),求树的最大直径
答案 = 连通数 - 树的最大直径 - 1
PS:
输入存在重边,误删;当两个点由两条边相连时,这两个点连通(因为把重边删了,比赛的时候卡死在那里了)tarjan 算法标记‘边’(无向图)
求树的最大直径,使用一个定理,需要两次求最短路
#pragma comment(linker, "/STACK:10240000000000,10240000000000") // hdu 人工扩栈
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
const int maxn = 202000;
const int MAXN = 2100000;
const int inf = 1000000000;
int min( int a, int b ){ return a < b ? a : b; }
vector<int>dedge[maxn];
struct Edge{
int v, next, sign;
}edge[MAXN];
int tot, head[maxn];
int n, m;
void init()
{
int i;
for( i = 0; i <= n; i++ )dedge[i].clear();
tot = 0;
memset( head, -1, sizeof(head) );
}
void add_edge( int u, int v )
{
edge[tot].v = v;
edge[tot].sign = 0;
edge[tot].next = head[u];
head[u] = tot++;
edge[tot].v = u;
edge[tot].sign = 0;
edge[tot].next = head[v];
head[v] = tot++;
}
int tmpdfn, dfn[maxn], low[maxn], inst[maxn], belong[maxn], st[maxn], top, scnt;
// tarjan算法(无向图)
void tarjan( int u )
{
int v, i, t;
low[u] = dfn[u] = tmpdfn++;
st[top++] = u;
inst[u] = 1;
for(i = head[u]; i != -1; i = edge[i].next)if( !edge[i].sign )
{
v = edge[i].v;
edge[i].sign = edge[i^1].sign = 1;
if( dfn[v] == -1 )
{
tarjan( v );
low[u] = min( low[u], low[v] );
if( low[v] > dfn[u] );//桥
}
else if( inst[v] )low[u] = min( low[u], dfn[v] );
}
if( low[u] == dfn[u] )
{
do{ belong[t = st[--top]] = scnt; inst[t] = 0; }while( t != u );
scnt++;
}
}
// 无向图缩点
void SCC()
{
int i;
top = 0;
tmpdfn = scnt = 1;
memset( dfn, -1, sizeof(dfn) );
memset( inst, 0, sizeof(inst) );
for(i = 1; i <= n; i++)if( dfn[i] == -1 )tarjan( i );
}
// 建立所点以后的图(树)
void rebuild()
{
int u, v, i;
for(u = 1; u <= n; u++)
{
for(i = head[u]; i != -1; i = edge[i].next)
{
v = edge[i].v;
if( belong[u] != belong[v] )
dedge[belong[u]].push_back( belong[v] );
}
}
}
// spfa深搜的写法
int dist[maxn];
void spfa_dfs( int u )
{
int i, v, size;
size = dedge[u].size();
for(i = 0; i < size; i++)
{
v = dedge[u][i];
if( dist[v] > dist[u] + 1 )
{
dist[v] = dist[u] + 1;
spfa_dfs( v );
}
}
}
// 求树的最大直径,两次spfa
int treelength()
{
int i, id;
fill( dist, dist+n+1, inf );
dist[1] = 0;
spfa_dfs( 1 );
for(id = 1, i = 2; i <= scnt - 1; i++)if( dist[i] > dist[id] )
id = i;
fill( dist, dist+n+1, inf );
dist[id] = 0;
spfa_dfs( id );
for(id = 1, i = 2; i <= scnt - 1; i++)if( dist[i] > dist[id] )
id = i;
return dist[id];
}
int main()
{
int u, v, i, j, maxlen;
while( ~scanf( "%d%d", &n, &m ), n + m )
{
init();
for( i = 0; i < m; i++ )
{
scanf( "%d%d", &u, &v );
add_edge( u, v );
}
SCC();
rebuild();
maxlen = treelength();
printf( "%d\n", (scnt - 1) - (maxlen + 1) );
}
return 0;
}