题意:一个连通无向图,问你增加一条边后,让原图桥边最少
分析:先边双缩点,因为连通,所以消环变树,每一个树边都是桥,现在让你增加一条边,让桥变少(即形成环)
所以我们选择一条树上最长的路径,连接两端,这样减少的桥边,最多,所以就是求树的直径
注:这题有重边,所以边双缩点也需要用重边版的
#include <stdio.h> #include <string.h> #include <algorithm> #include <math.h> #include <vector> #include <stack> using namespace std; typedef long long LL; const int N = 2e5+5; int head[N],tot,n,cnt,m,x[N*10],y[N*10]; struct Edge{ int v,next; }edge[N*10]; void add(int u,int v){ edge[tot].v=v; edge[tot].next=head[u]; head[u]=tot++; } int dfn[N],low[N],clk,bel[N],d[N]; stack<int>s; void targin(int u,int f){ dfn[u]=low[u]=++clk; s.push(u); bool flag=0;//判断重边 for(int i=head[u];~i;i=edge[i].next){ int v=edge[i].v; if(v==f&&!flag){ flag=1;//记录重边出现次数,只跳过父边第一次出现 continue; } if(!dfn[v]){ targin(v,u); low[u]=min(low[u],low[v]); } else if(dfn[v]<low[u])low[u]=dfn[v]; } if(dfn[u]==low[u]){ ++cnt; int k; do{ k=s.top(); s.pop(); bel[k]=cnt; }while(k!=u); } } int S,tmp,ans; void dfs1(int u,int f){ d[u]=d[f]+1; if(d[u]>tmp){ tmp=d[u]; S=u; } for(int i=head[u];~i;i=edge[i].next){ int v=edge[i].v; if(v==f)continue; dfs1(v,u); } } void dfs2(int u,int f){ d[u]=d[f]+1; ans=max(ans,d[u]); for(int i=head[u];~i;i=edge[i].next){ int v=edge[i].v; if(v==f)continue; dfs2(v,u); } } int main(){ d[0]=-1; while(~scanf("%d%d",&n,&m)){ if(!n&&!m)break; for(int i=1;i<=n;++i){ head[i]=-1; dfn[i]=low[i]=0; } cnt=clk=tot=0; for(int i=1;i<=m;++i){ scanf("%d%d",&x[i],&y[i]); add(x[i],y[i]),add(y[i],x[i]); } targin(1,-1); if(cnt<=2){ printf("0 "); continue; } for(int i=1;i<=cnt;++i)head[i]=-1; tot=0; for(int i=1;i<=m;++i){ int k1=bel[x[i]],k2=bel[y[i]]; if(k1==k2)continue; add(k1,k2),add(k2,k1); } tmp=ans=0; dfs1(1,0); dfs2(S,0); printf("%d ",cnt-1-ans); } return 0; }