传送门:P1536 村村通
人间风起,四季同书。(还是一篇 817 的做题记录la~)
题意:
有好多组数据,每组数据给你 m 条无向边的信息(u,v);
问你最少再添加多少条边就能使整张图连通。
思路:
首先我们要知道,一个图如果连通,边的数量最少是 n-1;
但是题目会出现这样一种情况:
n = 4,m = 3;
1 <——> 2 , 2 <——> 3 , 3 <——> 1;
这时候整张图变成了一个环和一个孤点,整张图并不联通
我们再观察会发现:
1 与 3 相连之前,1 与 3 就已经属于一个连通块了
换句话说就是 最后一条边加与不加是等效的
特殊 --> 一般:
如果两个点在添加这一条边之前已经属于同一连通块,就可以看作没有这条边
很自然地想到 并查集 就是求 kruscal 的东东
就是在读入两个点时,用 ans 记录此时加入图中对连通性真正有用的边的数量
- 如果 他们不属于同一个连通块,那么这条边有用,将两个点所在的连通块合并,ans+1;
- 如果 他们已经属于同一个连通块,那么这条边没用,不做任何处理
就转换成:
并查集 合并两个连通块、查询祖先
还是很好实现的啦 !
最终代码:
- 注意这个
非常神经有意思的输入 - 并查集查寻祖先时,路径压缩(我的爸爸的祖先就是我的祖先)
- 最终答案是 n-1(图中应当有的有用边的数量)与 ans(现在图中有用边的数量)的差
#include<bits/stdc++.h>
using namespace std;
const int maxn=1010;
int m,n;
int to[maxn];
int ans;
int go(int p)
{
if(p==to[p])return to[p];
else return to[p]=go(to[p]);//路径压缩
}
int main()
{
while(1)
{
ans=0;
cin>>n;
if(n==0)return 0;//注意一下这个奇葩的读入
cin>>m;
for(int i=1;i<=n;i++)
{
to[i]=i;
}
for(int i=1;i<=m;i++)
{
int x,y;
cin>>x>>y;
if(go(x)!=go(y))//还不属于一个并查集
{
//合并
ans++;
to[go(x)]=to[go(y)];
}
}
cout<<n-1-ans<<endl;
}
return 0;
}
后记:
- 这道题目主要考察了 并查集 的有关知识
非常板子。 - 当然也能用 kruscal 将每个边的边权置为 1 ,求一边最小生成树,再用 n-1-树的大小(其实就是多开一个结构体,基本方法还是并查集)计算还应当修建的路径条数。