• 集合并查集(知识与题目)


    集合的存储

    1、数组

    2、STL的vector

    3、链表(时间不够优秀)或者STL的list

    4、森林存储,也就是fa[]

    优化技术

    1、启发式合并

    每次将较小的集合合并到较大的集合中去,大小可以是size[],也可以是深度de[],或者是其他的

    2、路径压缩

    int fa[maxn],size[maxn];
    int find(int x){
    	if(x==fa[x]) return x;
    	return fa[x]=find(fa[x]);  //路径压缩 
    }
    void onion(int x,int y){
    	int fax=find(x);
    	int fay=find(y);
    	if(size[fax]>size[fay]) swap(x,y);  //启发式合并 
    	fa[fax]=fay;
    	size[fay]+=size[fax];
    }
    

    应用

    1、亲戚

    https://www.luogu.com.cn/problem/P1551

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<stack>
    #include<cstdio>
    #include<queue>
    #include<map>
    #include<vector>
    #include<set>
    using namespace std;
    const int maxn=5010;
    const int INF=0x3fffffff;
    int fa[maxn];
    int n,m,p,x,y;
    int find(int x){
    	if(x==fa[x]) return x;
    	else return fa[x]=find(fa[x]);
    }
    void onion(int x,int y){
    	int fa1=find(x);
    	int fa2=find(y);
    	if(fa1!=fa2) fa[fa1]=fa2;
    }
    int main(){
    	scanf("%d %d %d",&n,&m,&p);
    	for(int i=1;i<=n;i++) fa[i]=i;
    	for(int i=1;i<=m;i++){
    		scanf("%d %d",&x,&y);
    		onion(x,y);
    	}
    	for(int i=1;i<=p;i++){
    		scanf("%d %d",&x,&y);
    		if(find(x)!=find(y)) printf("No
    ");
    		else printf("Yes
    ");
    	}
    return 0;
    }
    

    2、银河英雄传说

    https://www.luogu.com.cn/problem/P4847

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<stack>
    #include<cstdio>
    #include<queue>
    #include<map>
    #include<vector>
    #include<set>
    using namespace std;
    const int maxn=30010;
    const int INF=0x3fffffff;
    //在线查询的题目
    //有的需要路径压缩,有的不需要
    int fa[maxn],size[maxn],dis[maxn];
    //dis[]就是每个点距离它的fa的距离,不能进行压缩
    int find(int x){
    	if(x==fa[x]) return x;
    	int ff=find(fa[x]);
    	dis[x]+=dis[fa[x]];  //没有压缩 
    	return fa[x]=ff; //压缩   !!!!!这里是关键 
    } 
    void onion(int x,int y){
    	int fa1=find(x);
    	int fa2=find(y);
    	if(fa1==fa2) return;
    	fa[fa1]=fa2;
    	dis[fa1]+=size[fa2]; //把fa1连在fa2后面
    	size[fa2]+=size[fa1]; //整个队伍的长度 !!!!这里是关键 
    }
    int t; 
    int main(){
    	scanf("%d",&t);
    	char a[10];   //不要用char a,因为还要控制输入很麻烦 
    	int x,y;
    	for(int i=1;i<=30000;i++) {
    		fa[i]=i;
    		size[i]=1;
    	} //不要忘了初始化 
    	//getchar();
    	while(t--){
    		scanf("%s",a);
    		if(a[0]=='M'){
    			scanf("%d %d",&x,&y);
    			onion(x,y);
    		}
    		else {
    		scanf("%d %d",&x,&y);
    		int fa1=find(x),fa2=find(y);
    		if(fa1!=fa2){
    			printf("-1
    ");
    		} 
    		else{
    			printf("%d
    ",abs(dis[x]-dis[y])-1);
    		}
    	}
    		//getchar();
    	}
    return 0;
    }
    

    3、关押罪犯

    https://www.luogu.com.cn/problem/P1525

    合并敌人的敌人与自己,并且注意敌人的表示方法,x的敌人是x+n

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<stack>
    #include<cstdio>
    #include<queue>
    #include<map>
    #include<vector>
    #include<set>
    using namespace std;
    const int maxn=20010;
    const int mm=100001;
    const int INF=0x3fffffff;
    //用贪心的思想,把仇恨值从大到小排序
    //然后让排在前面的仇恨值最大的x,y放在两个集合里面,同时维护x的敌人x',y的敌人y',在分开x,y的时候,要合并x和y',x'和y
    //在合并的时候如果 x和y'或者x'和y已经在一个集合里面了,那么此时的仇恨值就是最小的 
    struct node{
    	int x,y,w;
    	bool operator <(const node& x)const{
    		return w>x.w ; //注意重载小于符号的写法 
    	}
    }a[mm];  //存储的是关系
    int n,m;
    int fa[2*maxn];  //为什么要*2???因为!!!!x的敌人表示为x+n,注意这种表示方法
    int find(int x){
    	if(x==fa[x]) return x;
    	else return fa[x]=find(fa[x]);
    } 
     
    int main(){
    	scanf("%d %d",&n,&m);
    	for(int i=1;i<=2*n;i++) fa[i]=i;
    	for(int i=1;i<=m;i++){
    		scanf("%d %d %d",&a[i].x,&a[i].y,&a[i].w);
    	}
    	sort(a+1,a+1+m);
    	for(int i=1;i<=m;i++){
    		int x=a[i].x,y=a[i].y,w=a[i].w;
    		int fa1=find(x),fa2=find(y);
    		if(fa1==fa2){ //如果在一个集合里面 
     			printf("%d
    ",w); //就输出当前的仇恨值
    			 return 0; 
    		}
    		else{
    			fa[fa1]=find(y+n);  //注意写法啊啊啊 
    			fa[fa2]=find(x+n);  
    		}
    	}
    	printf("0
    ");
    return 0;
    }
    

    4、食物链

    https://www.luogu.com.cn/problem/P2024

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<stack>
    #include<cstdio>
    #include<queue>
    #include<map>
    #include<vector>
    #include<set>
    using namespace std;
    const int maxn=50004;
    const int INF=0x3fffffff;
    int fa[maxn*3]; //x表示x,x+n表示x吃的,x+2*n表示吃x的 
    int find(int x){
    	if(x==fa[x]) return x;
    	return fa[x]=find(fa[x]);
    } 
    void oni(int x,int y){
    	int aa=find(x);
    	int aa1=find(y);
    	if(aa!=aa1) fa[aa]=fa[aa1];
    }
    bool same(int x,int y){
    	return find(x)==find(y);  //表示x与y是不是在一个集合 
    }
    int n,k,flag,x,y;
    int main(){
    	int ans=0;
    	scanf("%d %d",&n,&k);
    	for(int i=1;i<=3*n;i++) fa[i]=i;
    	while(k--){
    		scanf("%d %d %d",&flag,&x,&y);
    		if(x>n||y>n){
    			ans++;
    			continue;
    		}
    		if(flag==1){  //x与y是不是捕食关系 
    			if(same(x+n,y)||same(x+2*n,y)){
    				ans++;
    			}
    			else{
    				oni(x,y);
    				oni(x+n,y+n);
    				oni(x+2*n,y+2*n);
    			}
    		}
    		else if(flag==2){  //x吃y 
    		if(x==y){
    			ans++;
    			continue;
    		}
    			if(same(x,y)||same(x+2*n,y)){
    				ans++;
    			}
    			else{
    				oni(x,y+2*n);  //
    				oni(x+n,y);
    				oni(x+2*n,y+n);
    			}
    		}
    	}
    	printf("%d
    ",ans);
    return 0;
    }
    

      

  • 相关阅读:
    git撤销修改
    python参数组合
    java打包jar后,使之一直在linux上运行,不随终端退出而关闭
    输入流加载资源文件的3种方式
    ActiveMQ集群下的消息回流功能
    activemq在一台服务器上启动多个Broker
    JAVA多线程下载
    829. 连续整数求和-leetcode
    mysql笔记-索引
    redis源码学习-skiplist
  • 原文地址:https://www.cnblogs.com/shirlybaby/p/12287532.html
Copyright © 2020-2023  润新知