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;
}