并查集刷题整理
并查集刷题整理
并查集是一种数据结构,然而用于维护其的数组及函数又极少,用途极为广泛,被广泛地应用于极多的综合题目,
比较经典的应用就是最小生成树\(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\n",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\n",min(a,b));
return 0;
}