题目链接:https://vjudge.net/problem/HDU-4612
题意:给一个连通有重边的无向图,现在添加一条边,求剩余桥的最小数量。
参考:https://blog.csdn.net/babing2770/article/details/102076878
思路:先求边双然后缩点,然后重新建图,其中每条边都是原图中的桥,只要在树的直径的两端连一条线,所能消除的桥的数量是最多的。
树的直径可以用dfs或bfs求,思想都是任取一点a进行搜索,寻找距离点a最远的点b,然后再从点b开始搜索,寻找距离点b最远的点c,bc的间距就是树的直径。
此题对于内存限制的非常多,不能用vector保存边,只能用邻接表。对于重边,map或set判重都会爆内存,遍历时要去掉v!=fa的判断以及只标记当前边及其反向边,不限制其他重边,详见代码。
对于桥的标记,map和set c++能过但是g++爆内存。
#include <cstdio>
#include <algorithm>
#include <queue>
#include <vector>
#include <cstring>
#include <iostream>
#include <cmath>
#include <set>
#include <fstream>
#include <list>
#include <stack>
#include <map>
using namespace std;
const int maxn=2e5+5;
int pre[maxn],low[maxn],sccno[maxn],d[maxn],head[maxn],vis[maxn*10];
int dfs_clock,n,m,scc_cnt,bridge,maxway,pos;
int cnt1,cnt2;
map <int,int> vis1;
struct EDGE
{
int u,v,next,id;
} edge1[maxn*10],edge2[maxn*10];
void add1(int u,int v)
{
edge1[cnt1].u=u;
edge1[cnt1].v=v;
edge1[cnt1].next=head[u];
head[u]=cnt1++;
}
void add2(int u,int v)
{
edge2[cnt2].u=u;
edge2[cnt2].v=v;
edge2[cnt2].next=head[u];
head[u]=cnt2++;
}
void init()
{
dfs_clock=0;
scc_cnt=0;
cnt1=cnt2=0;
bridge=0;
memset(pre,0,sizeof(pre));
memset(vis,0,sizeof(vis));
memset(low,0,sizeof(low));
memset(head,-1,sizeof(head));
memset(sccno,0,sizeof(sccno));
vis1.clear();
}
int dfs(int u,int fa)//寻找桥
{
int lowu=pre[u]=++dfs_clock;
int ch=0;
for(int i=head[u]; i!=-1; i=edge1[i].next)
{
int v=edge1[i].v;
if(vis[i])
continue;
vis[i]=vis[i^1]=1;//标记当前边和反向边
if(!pre[v])
{
ch++;
int lowv=dfs(v,u);
lowu=min(lowu,lowv);
if(lowv>pre[u])
{
vis1[i]=vis1[i^1]=1;
bridge++;
}
}
else if(pre[v]<pre[u])//去掉了v!=fa的判断 因为有重边因此v可以等于fa
{
lowu=min(lowu,pre[v]);
}
}
low[u]=lowu;
return lowu;
}
void suodian(int cur,int no)//缩点
{
sccno[cur]=no;
for(int i=head[cur]; i!=-1; i=edge1[i].next)
{
int v=edge1[i].v;
if(sccno[v]||vis1[i]==1)
continue;
suodian(v,no);
}
}
void bfs(int u)//树的直径
{
queue<int> q;
memset(d,0,sizeof(d));
memset(vis,0,sizeof(vis));
q.push(u);
vis[u]=1;
maxway=0;
pos=u;
while(!q.empty())
{
u=q.front();
q.pop();
for(int i=head[u]; i!=-1; i=edge2[i].next)
{
int v=edge2[i].v;
if(vis[v])
continue;
vis[v]=1;
d[v]=d[u]+1;
if(d[v]>maxway)
{
maxway=d[v];
pos=v;
}
q.push(v);
}
}
}
int main()
{
while(scanf("%d%d",&n,&m))
{
if(n==m&&!n)
break;
init();
for(int i=0; i<m; i++)
{
int x,y;
scanf("%d%d",&x,&y);
add1(x,y);
add1(y,x);
}
dfs(1,-1);
for(int i=1; i<=n; i++)
{
if(!sccno[i])
{
suodian(i,++scc_cnt);
}
}
memset(head,-1,sizeof(head));
for(int i=0; i<cnt1; i+=2)//重新建图
{
if(sccno[edge1[i].u]!=sccno[edge1[i].v])
{
add2(sccno[edge1[i].u],sccno[edge1[i].v]);
add2(sccno[edge1[i].v],sccno[edge1[i].u]);
}
}
bfs(sccno[1]);
bfs(pos);
printf("%d\n",bridge-maxway);
}
return 0;
}