并查集(题目来着寒训)

题目:

&1:P3367 【模板】并查集

&2:P1111 修复公路

&3:P1551 亲戚

&4:P2256 一中校运会之百米跑

&5:P2078 朋友

&6:P2814 家谱

并查集

并查集可分为 数字并查集和字符并查集 ,是一种数据结构。主要有以下两个作用:

  1. 动态地维护一些集合,支持合并集合和查询元素所属的集合。
  2. 用于判断图或树中的连通性,即判断两个节点之间是否存在路径相连。

先不论哪种类型,一般写成以下形式:

int fa[5];
int find(int x){
	if(fa[x]==x) return x;
	return fa[x]=find(fa[x]);
}
fa[find(b)]=find(a);

并查集由三部分组成 :fa数组   查询   合并

&1  其中查询为了降低时间复杂度,会写成以上代码中的形式,直观表达如下:

比如说查5,先前是o3,现在只需o1

&2  fa数组先把父亲赋成自己

&3  至于合并,容易理解,b的父亲是a

当然这里

fa[find(b)]=find(a);
fa[find(a)]=find(b);

两种形式都可以,为了方便理解,大都写成第一种

可能会有小伙伴有这个问题——如果find(4)的结果是 return fa[4]=2 那find(4)应该输出什么

这种情况会先对fa[4]赋值成2,再return fa[4]

数字并查集

int fa[5];
int find(int x){
	if(fa[x]==x) return x;
	return fa[x]=find(fa[x]);
}
int main()
{
	int i;
	for(i=1;i<5;i++) fa[i]=i;
	cin>>合并次数; 
	while(合并次数--)
	{
		cin>>a>>b;
		fa[find(b)]=find(a);
	}
	return 0;
}

这个类型没什么好提的,整形浮点型都可以写

字符并查集

字符串类型因为要先对fa数组赋成自己,所以引入map赋值

#include<map>

map<string,string> fa;
string find(string x){
	if(fa[x]==x) return x;
	return fa[x]=find(fa[x]);
}
cin>>a;
fa[a]=a;

其实也没啥,只需改变类型,定义字符map

例1:P3367 【模板】并查集

例1:P3367 【模板】并查集

思路:够模板吧,直接上操作

代码:

#include<iostream>
using namespace std;

int fa[10005];
int find(int x){
	if(fa[x]==x) return x;
	return fa[x]=find(fa[x]);
}
int main()//3367
{
	int i,n,m,a,b,c;
	cin>>n>>m;
	for(i=1;i<=n;i++) fa[i]=i;
	while(m--)
	{
		cin>>a>>b>>c;
		if(a==1) fa[find(c)]=find(b);
		else
		{
			b=find(b),c=find(c);
			if(b==c) cout<<"Y"<<endl;
			else cout<<"N"<<endl;
		}
	}
	return 0;
}

例2:P1111 修复公路

例2:P1111 修复公路

思路:先按修路时间排序,这个修路的过程就相当于连接村庄的过程

任意理解——如果两个村庄的父节点不同就修,同就不修

注意——n个村庄想要任意两个村庄通路,最少需要修n-1条路

代码:

#include<iostream>
#include<algorithm>
using namespace std;

struct P{
	int x,y,t;
}s[100005];
int fa[1005];
int find(int x){
	if(fa[x]==x) return x;
	return fa[x]=find(fa[x]);
}
bool cmp(P a,P b){
	return a.t<b.t;
}
int main()//1111
{
	int n,m,i,a,b,sum=0;
	cin>>n>>m;
	for(i=1;i<=n;i++) fa[i]=i;
	for(i=1;i<=m;i++) cin>>s[i].x>>s[i].y>>s[i].t;
	sort(s+1,s+m+1,cmp);
	for(i=1;i<=m;i++)
	{
		a=s[i].x,b=s[i].y;
		if(find(a)!=find(b))
		{
			fa[find(a)]=find(b);
			sum++;
		}
		if(sum==n-1)
		{
			cout<<s[i].t;
			return 0;
		}
	}
	cout<<"-1";
	return 0;
}


例3:P1551 亲戚

P1551 亲戚

思路:与例1差不多的模板题

代码:

#include<iostream>
using namespace std;

int fa[5005];
int find(int x){
	if(fa[x]==x) return x;
	return fa[x]=find(fa[x]);
}
int main()//1551
{
	int n,m,p,i,a,b;
	cin>>n>>m>>p;
	for(i=1;i<=n;i++) fa[i]=i;
	for(i=0;i<m;i++)
	{
		cin>>a>>b;
		fa[find(b)]=find(a);
	}
	for(i=0;i<p;i++)
	{
		cin>>a>>b;
		if(find(a)==find(b)) cout<<"Yes"<<endl;
		else cout<<"No"<<endl;
	}
	return 0;
}

例4:P2256 一中校运会之百米跑

例4:P2256 一中校运会之百米跑

思路:字符并查集的模板题

代码:

#include<iostream>
#include<map>
using namespace std;

map<string,string> fa;
string find(string x){
	if(fa[x]==x) return x;
	return fa[x]=find(fa[x]);
}
int main()//2256
{
	int n,m,k,i;
	string a,b;
	cin>>n>>m;
	for(i=1;i<=n;i++) 
	{
		cin>>a;
		fa[a]=a;
	}
	for(i=0;i<m;i++)
	{
		cin>>a>>b;
		fa[find(b)]=find(a);
	}
	cin>>k;
	for(i=0;i<k;i++)
	{
		cin>>a>>b;
		if(find(a)==find(b)) cout<<"Yes."<<endl;
		else cout<<"No."<<endl; 
	}
	return 0;
}

例5:P2078 朋友

例5:P2078 朋友

思路:无非就是再开一个fa数组的问题

但是留意

if(finda(i)==finda(1)) suma++;
if(findb(i)==findb(1)) sumb++;

find(1)比较而不是直接与1比较,这是因为输入的问题——2 1&&1 2导致父亲不一样

代码:

#include<iostream>
using namespace std;

int fa[10005],fb[10005];
int finda(int x)
{
	if(fa[x]==x) return x;
	return fa[x]=finda(fa[x]);
}
int findb(int x){
	if(fb[x]==x) return x;
	return fb[x]=findb(fb[x]);
}
int main()//2078
{
	int n,m,p,q,i,a,b,suma=0,sumb=0;
	cin>>n>>m>>p>>q;
	for(i=1;i<=n;i++) fa[i]=i;
	for(i=1;i<=m;i++) fb[i]=i;
	for(i=0;i<p;i++)
	{
		cin>>a>>b;
		fa[finda(b)]=finda(a);
	}
	for(i=0;i<q;i++)
	{
		cin>>a>>b;
		a=0-a,b=0-b;
		fb[findb(b)]=findb(a);
	}
	for(i=1;i<=max(m,n);i++)
	{
		if(finda(i)==finda(1)) suma++;
		if(findb(i)==findb(1)) sumb++;
	}
	cout<<min(suma,sumb);
	return 0;
}

例6:P2814 家谱

例6:P2814 家谱

思路:因为题目输入的问题,我们需要把父亲赋给儿子,剩下就是普普通通的并查集了

char类型只读取一个,所以我们可以对输入的字符串进行分开,这样写来方便很多

代码:map的映射值是  空或0,详见代码注释

#include<iostream>
#include<map>
using namespace std;

map<string,string> fa;//字符串类型 
string find(string x){
	if(fa[x]==x) return x;
	return fa[x]=find(fa[x]);
}
int main()//2814
{
	char ch;
	string s,com;
	cin>>ch;
	while(ch!='$')
	{
		cin>>s;
		if(fa[s]=="") fa[s]=s;//字符串类型 双引 不能加空格 
		if(ch=='#') com=s;	
		else if(ch=='+') fa[s]=com;
		else cout<<s<<" "<<find(s)<<endl;
		cin>>ch;
	}
	return 0;
}

  • 24
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值