题目大意很简单:给定一个双向连通的公路网,当某些公路路段检修的时候可能会由于该段公路不通,可能会使某些旅游点之间无法通行,求至少新建多少条公路,使得任意对一段公路进行检修的时候,所有的旅游景点之间仍然畅通;
分析:检修某一路段导致公路网不畅通的原因必然是该段公路在图中是一桥,因此,完全畅通的方法就是,家最若干条边,使图中不存在桥。先找出所有的桥,将双连通分量进行缩点,得到一个树形图,将所有的叶子结点和根节点依次新加边链接起来,于是新加边的条数即是(度为1的点数目+1)/2,.考虑到题目只要求求度为1的点数目,因此可以部分缩点,利用并查集,保存每个桥边的顶点,统计每个顶点在并查集中的代表元的度数即可
#include<stdio.h>
#include<memory.h>
#define N 1000
#define init(a) memset(a,0,sizeof(a))
#define min(a,b) ((a)<(b)?(a):(b))
struct Edge
{
int dest;
Edge *next;
};
Edge *E[N+1],mempool[N*2];
int anc[N+1],deep[N+1],used[N+1],root[N+1],bridge[N+1][2];
int n,m,memh,bridge_n;
int part[N+1],part_n,d[N+1];
void addEdge(int u,int v)
{
Edge *L=&mempool[memh++];
L->dest=v;
L->next=E[u];
E[u]=L;
}
void readData()
{
int i,j;
init(E),memh=0;
scanf("%d%d",&n,&m);
while(m--)
{
scanf("%d%d",&i,&j);
addEdge(i,j);
addEdge(j,i);
}
}
void make_set()
{
for(int i=1;i<=n;i++)
root[i]=i;
}
int find_set(int s)
{
int r,t=s;
while(t!=root[t])
t=root[t];
while(s!=t)
{
r=root[s];
root[s]=t;
s=r;
}
return t;
}
void union_set(int u,int v)
{
int p1=find_set(u);
int p2=find_set(v);
if(p1!=p2)
root[p2]=p1;
}
void dfs_con(int s,int pre,int d)
{
anc[s]=deep[s]=d;
used[s]=1;
for(Edge *L=E[s];L;L=L->next)
{
int j=L->dest;
if(j!=pre&&used[j]==1)
anc[s]=min(anc[s],deep[j]);
if(!used[j])
{
dfs_con(j,s,d+1);
anc[s]=min(anc[s],anc[j]);
if(anc[j]<=deep[s])
union_set(j,s);
else
{
bridge[++bridge_n][0]=s;
bridge[bridge_n][1]=j;
}
}
}
used[s]=2;
}
void connex()
{
int i,j;
init(used);
make_set();
bridge_n=0;
for(i=1;i<=n;i++)
if(!used[i])
dfs_con(i,0,1);
}
int work()
{
int i,j,count=0;
init(d);
for(i=1;i<=bridge_n;i++)
{
j=find_set(bridge[i][0]);
d[j]++;
j=find_set(bridge[i][1]);
d[j]++;
}
for(i=1;i<=n;i++)
if(d[i]==1)
count++;
return (count+1)/2;
}
int main()
{
readData();
connex();
printf("%d/n",work());
return 0;
}