题目链接
题意:给你一张图,问你 加了一条边之后,最少有多少桥。
如果那些形成一个环的点,点之间的边都不是桥,所以要考虑把这些点缩成一个点。
缩点完成后,就会形成一棵树。找到连接起来最长的边(树的直径),在用一条边把他们从头到尾连起来,那样就去掉了最多的边,剩下的就是我们要求的 桥;
总的来说就是,无向图缩点,然后形成树,找树的直径,再拿边的个数减去树的直径就是答案。
找树的直径,一定要走两次 dfs 找到底层的点,在走一次 dfs 才是数的直径。
#include <bits/stdc++.h>
using namespace std;
#define mem(x,v) memset(x,v,sizeof(x))
const int N = 5e5 + 10;
int n,m,head[N],cnt;
struct node{
int b,next;
}g[N];
stack<int>q;
vector<int>f[N];
int dfn[N],low[N],times,cont,scc[N],d[N];
bool instk[N];
void add(int x, int y){
g[++cnt].next = head[x];
g[cnt].b = y;
head[x] = cnt;
return;
}
void tarjan(int u, int fa){
low[u] = dfn[u] = ++times;
q.push(u);
for (int i = head[u]; i; i = g[i].next){
int v = g[i].b;
if (v == fa) continue;
if (!dfn[v]) {
tarjan(v,u);
low[u] = min(low[u],low[v]);
} else low[u]= min(low[u],dfn[v]);
}
if (low[u] == dfn[u]){
cont++;
while(true){
int v = q.top(); q.pop();
scc[v] = cont;
if (v == u) break;
}
}
return;
}
void dfs(int x,int y){
d[x] = d[y]+1;
instk[x] = 1;
for (int i = 0; i < f[x].size(); i++)
if (f[x][i] != y && !instk[f[x][i]]) dfs(f[x][i],x);
return;
}
int main(){
int T,x,y;
cin>>T;
while(T--){
cnt = 0;
mem(g,0);
mem(head,0);
scanf("%d%d",&n,&m);
for (int i = 0; i < m; i++){
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
times = 0;
cont = 0;
while(!q.empty()) q.pop();
for (int i = 0; i <= n; i++){
low[i] = dfn[i] = scc[i] = 0;
}
tarjan(1,0); //强联通。
for (int i = 0; i <= cont; i++)
f[i].clear();
for (int i = 1; i <= n; i++) //新建边。找树的直径。
for (int j = head[i]; j; j = g[j].next)
if (scc[i] != scc[g[j].b]){
f[scc[g[j].b]].push_back(scc[i]);
f[scc[i]].push_back(scc[g[j].b]);
}
int S = 0,ans = 0;
mem(instk,0);
dfs(1,0);
for (int i = 1; i <= cont; i++) if (d[i] > d[S]) S = i;
mem(instk,0);
dfs(S,0);
for (int i = 1; i <= cont; i++)
ans = max(ans,d[i]);
printf("%d\n",cont-ans);
}
return 0;
}