题意:
求加入一条边(双向)后,图中桥最小的数量。
知识点:
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; }