并查集
概念:
并查集由一个整型数组pre[ ]和两个函数find( )、join( )构成
数组pre[ ]记录了每个点的前导点是什么,函数find(x)用于查找,函数join(x,y)用于合并
作用:
并查集的主要作用是求连通分支数(如果一个图中所有点都存在可达关系(直接或间接相连),则此图的连通分支数为1;如果此图有两大子图各自全部可达,则此图的连通分支数为2……
int find(int x) //查找老大
{
while(pre[x] != x)
x = pre[x];
return x;
}
void join(int x,int y) //建立联系
{
int fx=find(x), fy=find(y);
if(fx != fy)
pre[fx]=fy;
}
算法优化
1.路径的压缩
int find_pre(int x) //查找结点x的根结点
{
if(pre[x] == x) //递归出口:x的上级为x本身,即x为根结点
return x;
return pre[x] = find_pre(pre[x]); //此代码相当于先找到根结点rootx,然后pre[x]=rootx
}
2.加权标记法
思路就是将权小的集合加到权大的集合中权不变,两集合权相同是相加权加一,从而减小树的深度。
应用
基本的判断道路,宗教等数量问题
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <string.h>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
int p[1000]={-1},l[1000]={0};
int find(int x){
int foot=x;
while(p[foot]!=-1)
foot=p[foot];
return foot;
}
void jiao(int x,int y){
int a=find(x);
int b=find(y);
if(a!=b){
if(l[a]==l[b]){
p[a]=b;
l[b]++;
}
else if(l[a]>l[b])
p[b]=a;
else if(l[a]<l[b])
p[a]=b;
}
}
int main(int argc, char** argv) {
int n,m,sum,x,y;
while(scanf("%d",&n)&&n){
memset(p,-1,sizeof(int)*1000);
memset(l,0,sizeof(int)*1000);
scanf("%d",&m);
sum=n-1;
while(m--){
scanf("%d%d",&x,&y);
jiao(x,y);
}
for(int i=1;i<=n;i++){
if(p[i]!=-1)
sum--;
}
printf("%d\n",sum);
}
return 0;
}
食物链捕食关系
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<algorithm>
using namespace std;
#define manx 50005
int p[3*manx];
int find (int x){
if(p[x]==x)return x;
else return p[x]=find(p[x]);
}
void jiao(int x, int y){
int t1=find(x);
int t2=find(y);
if(t1!=t2){
p[t1]=t2;
}
}
int main(){
int n,m,s=0,a,b,c;
scanf("%d%d",&n,&m);
for(int i=1;i<=3*n;i++){
p[i]=i;
}
for(int i=1;i<=m;i++){
scanf("%d%d%d",&a,&b,&c);
if(a==2&&b==c){
s++;continue;
}
if(b<1||b>n||c<1||c>n){
s++;continue;
}
if(a==1){
if(find(b)==find(c+n)||find(c)==find(b+n)){
s++;
}
else{
jiao(b,c);
jiao(b+n,c+n);
jiao(b+2*n,c+2*n);
}
}
else if(a==2){
if(find(b)==find(c)||find(c)==find(b+n)){
s++;
}
else{
jiao(b,c+n);
jiao(b+n,c+2*n);
jiao(b+2*n,c);
}
}
}
printf("%d",s);
return 0;
}
判断路径的距离
将不同集合相加成一个集合,同一个集合中判断距离与条件是否相同。距离的运算可以用向量的加法运算,即(a,b]+(b,c]=(a,c]。所以在计算使条件中的a必须-1。
#include <iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
const int maxn=200001;
int f[maxn];
int val[maxn];
int find(int t)
{
if(f[t]==-1)return t;
int ans=find(f[t]);
val[t]+=val[f[t]];
return f[t]=ans;
}
int main()
{
int n,m;
while(scanf("%d%d",&n,&m)!=EOF)
{
memset(f,-1,sizeof(f));
memset(val,0,sizeof(val));
int ans=0;
while(m--)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
a=a-1;
int t1=find(a);
int t2=find(b);
if(t1!=t2)
{
f[t2]=t1;
val[t2]=val[a]-val[b]+c;
}
else
{
if(val[b]-val[a]!=c)
ans++;
}
}
printf("%d\n",ans);
}
return 0;
}