借鉴这里:http://blog.csdn.net/softshell/article/details/9496161
有N个点,M条边(有重边)的无向图,这样图中会可能有桥,问加一条边后,使桥最少,求最少桥的数量。
缩点后,求出图中的桥的个数,然后重建图必为树,求出树的最长直径,在该直径的两端点连一边,则图中的桥会最少。
双联通缩点有很多不懂滴地方。。。不咋明白。。。问题之一。。vis数组用不用写。。唉。。唉。。
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<queue>
using namespace std;
#define clr(x) memset(x,0,sizeof(x))
#define maxn 300015
#define maxm 4000015
struct node{
int to,next;
}e[maxm],e2[maxm];
int head[maxn],tot;
int head2[maxn],tot2;
int col[maxn],dfn[maxn],low[maxn],sta[maxn];
int top,ti,scc; //scc 双联通分支个数
int n,m;
bool vis[maxn];
void init(){
memset(head,-1,sizeof(head));
clr(vis);
clr(dfn);
tot=0;
top=0;
scc=ti=0;
}
void add(int u,int v){
e[tot].to=v;
e[tot].next=head[u];
head[u]=tot++;
}
void add2(int u,int v){
e2[tot2].to=v;
e2[tot2].next=head2[u];
head2[u]=tot2++;
}
void tarjan(int u,int fa){
dfn[u]=low[u]=++ti;
sta[++top]=u;
vis[u]=true;
int cnt=0;
for(int i=head[u];i!=-1;i=e[i].next){
int v=e[i].to;
if(!dfn[v]){
tarjan(v,u);
low[u]=min(low[u],low[v]);
}
else if(fa==v){
if (cnt) low[u] = min(low[u],dfn[v]);//重边
cnt++;
}
else if(vis[v])low[u]=min(low[u],dfn[v]);
}
if(dfn[u]==low[u]){
int x;
scc++;
do{
x = sta[top--];
vis[x]=false;
col[x] = scc;
}while(x!=u);
}
}
void build(){
tot2 = 0;
memset(head2,-1,sizeof(head2));
for(int i = 1; i <= n; i++){
for(int j = head[i]; j!=-1; j = e[j].next){
int v = e[j].to;
if(col[i]!=col[v])
add2(col[i],col[v]);
}
}
}
int r;//直径
int bfs(int x){
queue<int>q;
int dis[maxn];
int end;
memset(dis,-1,sizeof(dis));
q.push(x);
dis[x]=0;
while(!q.empty()){
int u=q.front();
q.pop();
r=dis[u];
end=u;
for(int i=head2[u];i!=-1;i=e2[i].next){
int v=e2[i].to;
if(dis[v]==-1){
dis[v]=dis[u]+1;
q.push(v);
}
}
}
return end;
}
int main(){
int u,v;
while(scanf("%d%d",&n,&m)!=EOF){
init();
if(n==0&&m==0)break;
for(int i=0;i<m;i++){
scanf("%d%d",&u,&v);
if(v==u)continue;
add(u,v);
add(v,u);
}
tarjan(1,-1);
build();
r=0;
bfs(bfs(1));
printf("%d\n",scc-r-1);
}
return 0;
}