题目大意:求一个连通图然后加一条边使得桥的数目最少
题解思路:先把桥两边不是的点所有连通的点都缩成一个点;
然后把缩完的点构成一颗树那么再直径的两端加一条边就是最优方案
注意:判断重边
题目链接
#include<iostream>
#include<cstring>
#include<stack>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
const int mx = 2e5+10;
#pragma comment(linker,"/STACK:1024000000,1024000000") //扩大栈的容量
stack<int>s;
bool vis[mx];
int dis[mx];
vector<int>G1[mx],G2[mx];
int pre[mx],low[mx],sccno[mx];
int dfn,cnt,bri,n,m;
int last_node,maxs;
void init(){
memset(pre,0,sizeof(pre));
memset(sccno,0,sizeof(sccno));
memset(low,0,sizeof(low));
dfn = cnt = bri = 0;
for(int i = 1; i <= n; i++){
G1[i].clear();
G2[i].clear();
}
}
void dfs(int u,int fa){
pre[u] = low[u] = ++dfn;
s.push(u);
bool flag = 0;
for(int i = 0; i < G1[u].size(); i++){
int v = G1[u][i];
if(!pre[v]){
dfs(v,u);
low[u] = min(low[v],low[u]);
if(low[v] > pre[u])
bri++;
}
else if(v != fa || flag)
low[u] = min(low[v],low[u]);
if(v == fa) //判读之前是否有重边
flag = 1;
}
if(pre[u] == low[u]){
++cnt;
while(1){
int v = s.top();
s.pop();
sccno[v] = cnt;
if(v == u) break;
}
}
}
void bfs(int s){
queue<int>q;
q.push(s);
memset(vis,0,sizeof(vis));
vis[s] = 1;
dis[s] = 0;
last_node = s;
maxs = 0;
while(!q.empty()){
int u = q.front();
q.pop();
for(int i = 0; i < G2[u].size(); i++){
int v = G2[u][i];
if(!vis[v]){
vis[v] = 1;
dis[v] = dis[u]+1;
if(dis[v] > maxs){
maxs = dis[v];
last_node = v;
}
q.push(v);
}
}
}
}
void solve(){
dfs(1,-1);
for(int u = 1; u <= n; u++)
for(int i = 0; i < G1[u].size(); i++){
int v = G1[u][i];
if(sccno[u] != sccno[v]){
G2[sccno[u]].push_back(sccno[v]);
G2[sccno[v]].push_back(sccno[u]);
}
}
bfs(1); //先从任意点遍历最长的节点
bfs(last_node); //然后遍历回去求最长路径就是树的直径
printf("%d\n",bri-maxs);
}
int main(){
while(scanf("%d%d",&n,&m),n||m){
init();
for(int i = 1; i <= m; i++){
int u,v;
scanf("%d%d",&u,&v);
G1[u].push_back(v);
G1[v].push_back(u);
}
solve();
}
return 0;
}