寒假训练2并查集

并查集

概念:
并查集由一个整型数组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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值