并查集刷题整理

Posted 648-233

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了并查集刷题整理相关的知识,希望对你有一定的参考价值。

并查集刷题整理

并查集是一种数据结构,然而用于维护其的数组及函数又极少,用途极为广泛,被广泛地应用于极多的综合题目,

比较经典的应用就是最小生成树(kruskal)算法

T1:Watering Hole G

题意

(n)个牧场,需要挖井,在第(i)号农场挖需要(W_i)元,在(i)(j)号之间通水需要(P_{i,j}=P_{j,i})元,问最小花费.

思路:

显然这并不是最短路问题,是最小生成树问题,目的就是将所有农场通过花费最小的路径进行串通

对于"挖井"这个放在最小生成树中稍显生疏的操作,实际上通过建新点的操作,可以将挖井的初始花费转变成"地下水到井"的距离,以此将挖井的初始花费转变成边,进行(kruskal)

代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
const int N=305;
int n;
struct Ed{
	int from,to,dis;
};
bool operator<(const Ed &a,const Ed &b){
	return a.dis<b.dis;
}
bool operator>(const Ed &a,const Ed &b){
	return a.dis>b.dis;
}
priority_queue<Ed,vector<Ed>,greater<Ed> > q;
int fa[N];
inline int find(int x){
	if(fa[x]==x) return x;
	return fa[x]=find(fa[x]);
}
int main(){
	//freopen("P1550_2.in","r",stdin);
	scanf("%d",&n);
	for(int i=0;i<=n;++i) fa[i]=i;
	for(int i=1;i<=n;++i){
		int dis;
		scanf("%d",&dis);
		q.push((Ed){0,i,dis});
	}
	for(int i=1;i<=n;++i)
		for(int j=1;j<=n;++j){
			int dis;
			scanf("%d",&dis);
			if(i!=j)
				q.push((Ed){i,j,dis});
		}
	int ans=0;
	while(!q.empty()){
		Ed u=q.top();
		q.pop();
		int x=u.from,y=u.to;
		if(find(x)==find(y)) continue;
		ans+=u.dis;
		fa[fa[x]]=fa[y];
	}
	printf("%d
",ans);
	return 0;
}

p.s.

本题需要注意的是在进行按边权排序的时候,最好用小根堆,不要用结构体数组+(sort)函数,因为凿井价值的加入,并不好掌握边的数量,不方便对边进行排序,容易出错

T2:同学

题意:

(A)(B)两个公司,(A)公司全部是男员工,(B)全部是女员工,分别有(n),(m)个员工,,分别有(p),(q)条公司内的关系,问最多通过两公司的1号员工能够配成多少情侣

思路:

看上去这是二分图匹配,毕竟是求匹配方案,但是这就奇怪在两公司之间并没有关系,只是单纯通过两公司的1号进行交流,这样一来,将题意转化,就可以明确:

其实就是要求两公司内与1号员工有直接或间接关系的员工的数目的较小值

再次翻译:

在两边各跑一遍并查集看哪个公司中与1号员工属同一集合的员工数较小

代码:

#include<iostream>
#include<cstdio>
using namespace std;
const int N=20005;
int n,m,p,q;
int afa[N],bfa[N];
inline int afind(int x){
	if(afa[x]!=x) afa[x]=afind(afa[x]);
	return afa[x];
}
inline int bfind(int x){
	if(bfa[x]!=x) bfa[x]=bfind(bfa[x]);
	return bfa[x];
}
int main(){
	scanf("%d%d%d%d",&n,&m,&p,&q);
	for(int i=1;i<=n;++i) afa[i]=i;
	for(int i=1;i<=m;++i) bfa[i]=i;
	for(int i=1;i<=p;++i){
		int x,y;
		scanf("%d%d",&x,&y);
		if(afind(x)!=afind(y)) afa[afa[x]]=afa[y];
	}
	for(int i=1;i<=q;++i){
		int x,y;
		scanf("%d%d",&x,&y);
		x=-x;
		y=-y;
		if(bfind(x)!=bfind(y)) bfa[bfa[x]]=bfa[y];
	}
	int jostar=afind(1);
	int dio=bfind(1);
	int a=0,b=0;
	for(int i=1;i<=n;++i)
		if(afind(i)==jostar) ++a;
	for(int i=1;i<=m;++i)
		if(bfind(i)==dio) ++b;
	printf("%d
",min(a,b));
	return 0;
}

以上是关于并查集刷题整理的主要内容,如果未能解决你的问题,请参考以下文章

刷题方法:并查集

❤️数据结构入门❤️(2 - 5)- 并查集

leetcode之并查集+记忆化搜索+回溯+最小生成树刷题总结1

跟着chengyulala刷题之[kuangbin带你飞]之'并查集'专题/斜眼笑

并查集题目整理

并查集