并查集习题

Chemical_table(思维,并查集求连通块数⭐)

并查集的应用中,首先需要思考,并查集用于维护哪些节点构成的集合,再者就是分析,哪些节点该合并在同一个集合中
在这里插入图片描述

宗教信仰那题,比较明显,构成集合的节点是信仰着某个宗教的学生,信仰同一个总角的同学在同一集合

雪上漂移drift那题,构成集合的节点是雪堆的编号,能够任意相互到达的雪堆们在同一集合,虽然这题给出的是雪堆的坐标,但这只是用于判断是否合并的依据,归根结底还是雪堆之间的联通、雪堆构成集合
而化学元素周期表这题,需要m*n的方框中每个方格都填充到,已知构成矩形(平行于坐标轴)的四个点中三个,就可以补充剩下一个,求把矩阵填满最少需要添加多少点。
将行、列拆开构成二分图,矩阵的每个方块都要填充,相当于任意一个行号和任意一个列号都是相连的,怎样算相连,有一条直接的边是相连,有间接到达的边也算相连,比如,R1—C1, C1–R2, R1–C2,有这样三条边相连(横跨二部图的直接边),R2可通过C1、R1 与C2相连
明白填充所有方格到行序号与列序号的相连就好办了,就可以得知,构成集合的节点是行号和列号,通过给出的相连关系可以分化出连通块(行号与列号直接相连,合并后将发现不同行号之间也有相邻的关系,进而再推出没有直接相连边的行号列号直接是否联通,任何一个连通块中剩下的边都是可以直接加上的)
给出了一些直接边,就能得到二部图的联通信息(所有的连通块),要使得二部图两边任意联通,就需要连成一个连通块,需要加的边=连通块数-1

#include <bits/stdc++.h> 
using namespace std;
const int N=2e5+5;
int fa[N<<1];//要注意啦,组成集合的节点是行列序号,
//考虑的是行与行号、列与列号、行与列号之间的连通性,fa[m+n] 
int m,n,q; 
int find(int x){
	if(x==fa[x])return x;
	return fa[x]=find(fa[x]);
}
void merge(int x,int y){
	int fx=find(x);
	int fy=find(y);
	if(fx!=fy){
		fa[fx]=fy;
	}
}
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>m>>n>>q;
	for(int i=1;i<=m+n;i++)fa[i]=i;
	int x,y;
	for(int i=1;i<=q;i++){
		cin>>x>>y;
		merge(x,y+m);//行号列号属于不同的节点,要避免取相同的值
//		m+n个节点就用1~m+n的编号
//		行i对应编号i,列j对应编号m+j 
	}
	int res=0;
	for(int i=1;i<=m+n;i++){
		if(find(i)==i)res++;
//merge中只是挂在了父亲身上,并未改变直接父亲 ,需要调用一次find才能找到最终的父亲 
	} 
	cout<<res-1; 
	return 0;
}
//1 2
//2 2
//2 1
//行有1、2,列有1、2,行1行2列1列2这四个都属于同一连通块
//只是在表述时, 行1行2列1列2分别用编号1,2,3,4表示 



Ubiquitous_Religions

#include <iostream>
#include <map>
#include <algorithm>
using namespace std;
const int N=50005;
int fa[N];
int find(int x){
	if(x==fa[x])return x;
	return fa[x]=find(fa[x]);
}
map<int,int> mp;
int main(int argc, char** argv) {
//2:21
	int n,m;
	int t=0;
	while(cin>>n>>m){
		if(m==0&&n==0)break;
		t++;
		mp.clear();
		for(int i=1;i<=n;i++){
			fa[i]=i;
		}
		int x,y;
		for(int i=1;i<=m;i++){
			cin>>x>>y;
			int fx=find(x);
			int fy=find(y);
			if(fx!=fy)fa[fx]=fy;//要把fx所在的整个集合的编号改成fy	
		}
		for(int i=1;i<=n;i++){
			mp[find(i)]++;//注意不是mp[fa(i)]++;看上面那条注释,对于fx的父亲是直接变成fy
//			可是对于fx所有的孩子只是间接认fy为父亲,还需要进行一次find操作,才能把原先fx
//集合里所有的元素直接挂在fy节点下面	
//			mp.insert({})
		}
		cout<<"Case "<<t<<": "<<mp.size()<<endl;
	}
	return 0;
}



Ice_Skating

#include <bits/stdc++.h> 
#include <iostream> 
using namespace std;
const int N=105;	
int fa[N];
int n,m;
int find(int x){
	if(x==fa[x])return x;
	return fa[x]=find(fa[x]);
}
void merge(int x,int y){
	int fx=find(x);//错写成fx=fa[x]直接runtime error 
	int fy=find(y);
	if(fx!=fy){
		fa[fx]=fy;
	}
} 
struct node{
	int x,y;
}v[N];
//map<int,int> mp;
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	int n;
	cin>>n;
	for(int i=1;i<=n;i++){
		fa[i]=i;
	}
//	int x,y;
	for(int i=1;i<=n;i++){//将能相互到达的点合并为一个集合 
		cin>>v[i].x>>v[i].y;	 
	}	
	for(int i=1;i<=n;i++){ 
		int x=v[i].x;
		int y=v[i].y;
		for(int j=1;j<=n;j++){
			if(x==v[j].x||y==v[j].y){
				merge(i,j);
			}
		}	 
	}
//	for(int i=1;i<=n;i++){//以后不用map判断集合个数,居然mle 
//		mp[find(i)]++;
//	}
//	cout<<mp.size()-1;
 	int res=0;
 	for(int i=1;i<=n;i++){
 		if(find(i)==i)res++;
 	}
 	cout<<res-1;
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值