洛谷P1536 村村通

传送门: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-树的大小(其实就是多开一个结构体,基本方法还是并查集)计算还应当修建的路径条数。
posted @ 2024-08-17 17:17  lazy_ZJY  阅读(4)  评论(0编辑  收藏  举报