0
点赞
收藏
分享

微信扫一扫

并查集(C语言)

半夜放水 2022-01-20 阅读 110

先给一个并查集代码

并查集就是找一个团体,要用一个数组记录每一个元素的值,每个元素的初始值是数组的下标。

getf()函数可以找到相应下标最高领导人,f[]数组就是找到相应下标的上一级。merge函数是把a,b变成上下级关系。

我下面的思路都是在这个代码的基础上写的。

#include"stdio.h"
int f[5009]={0},n,m,j;
//初始化
void init()
{
	int i;
	for(i=1;i<=n;i++)
	f[i]=i;
	return;
}
//找上级,直到找到最后的最高领导人 
int getf(int v)
{
	if(f[v]==v)
	return v;
	else
 	{
		//路径压缩,每次在函数返回的时候,顺带把路上遇见的人的boss改成最后的找
		//到的祖宗编号,也就是最后的最高领导人编号,提升找最高领导人的速度
		f[v]=getf(f[v]);
		return f[v];	
	}	
} 
void merge(int v,int u)
{
	int t1,t2;//分别是v,u的最高领导人
	t1=getf(v);
	t2=getf(u);
	if(t1!=t2)
	{
		f[t2]=t1;
		//靠左原则,左边成为右边的boss,把右边的集合,作为左边集合的子集和	
	} 
	return; 
}
int main()
{
	int i,x,y,ans=0;
	scanf("%d%d",&n,&m);
	init(); 
	for(i=1;i<=m;i++)
	{
		scanf("%d%d",&x,&y);
		merge(x,y);	
	} 
	for(i=1;i<=n;i++)
	{
		if(getf(i)==i)
		ans++; 
	} 
	printf("%d",ans);
	return 0;
}

题目1:

题目描述

如题,现在有一个并查集,你需要完成合并和查询操作。

输入格式

第一行包含两个整数 N,M ,表示共有 N 个元素和 M 个操作。

接下来 M 行,每行包含三个整数 Zi​,Xi​,Yi​ 。

当 Zi​=1 时,将 Xi​ 与 Yi​ 所在的集合合并。

当 Zi​=2 时,输出 Xi​ 与 Yi​ 是否在同一集合内,是的输出 Y ;否则输出 N 。

输出格式

对于每一个 Zi​=2 的操作,都有一行输出,每行包含一个大写字母,为 Y 或者 N 。

输入输出样例

输入 #1复制

4 7
2 1 2
1 1 2
2 1 2
1 3 4
2 1 4
1 2 3
2 1 4

输出 #1复制

N
Y
N
Y

说明/提示

对于 30% 的数据,N≤10,M≤20。

对于 70% 的数据,N≤100,M≤10^3。

对于 100% 的数据,1≤N≤104,1≤M≤2×10^5,1≤Xi​,Yi​≤N,Zi​∈{1,2}。

题解:

判断一下输入的Zi​是1还是2,

        是1的话就调用merage函数让Xi​,Yi​ 有上下级关系。

        是2的话就判断Xi​,Yi​ 的最高领导人是否是同一个人。

代码如下

#include"stdio.h"
int f[100001]={0};
int n,m;
void init()
{
	int i;
	for(i=1;i<=n;i++)
	f[i]=i;
	return;
}
int getf(int i)//找最高领导人
{
	if(f[i]==i)
	return i;
	else 
	{
		f[i]=getf(f[i]);
		return f[i];	
	} 
} 
void merage(int a,int b)
{
	int t1,t2;
	t1=getf(a);
	t2=getf(b);
	if(t1!=t2)
	{
		f[t2]=t1;	
	}
	return ;	
}
int main()
{
	int i,a,b,c;
	scanf("%d %d",&n,&m);
	init();//初始化 
	for(i=1;i<=m;i++)
	{
		scanf("%d%d%d",&a,&b,&c);
		if(a==2)
		{
			if(getf(b)==getf(c))
			printf("Y\n");
			else
			printf("N\n");
		}
		if(a==1)
		merage(b,c);
	}
	return 0;
}

题目2:

题目背景

若某个家族人员过于庞大,要判断两个是否是亲戚,确实还很不容易,现在给出某个亲戚关系图,求任意给出的两个人是否具有亲戚关系。

题目描述

规定:xx 和 yy 是亲戚,yy 和 zz 是亲戚,那么 xx 和 zz 也是亲戚。如果 xx,yy 是亲戚,那么 xx 的亲戚都是 yy 的亲戚,yy 的亲戚也都是 xx 的亲戚。

输入格式

第一行:三个整数 n,m,p,(n,m,p≤5000),分别表示有 n 个人,m 个亲戚关系,询问 p 对亲戚关系。

以下 m 行:每行两个数 Mi​,Mj​,1≤Mi​, Mj​≤N,表示 Mi​ 和 Mj​ 具有亲戚关系。

接下来 p 行:每行两个数 Pi​,Pj​,询问 Pi​ 和 Pj​ 是否具有亲戚关系。

输出格式

p 行,每行一个 Yes 或 No。表示第 i 个询问的答案为“具有”或“不具有”亲戚关系。

输入输出样例

输入 #1复制

6 5 3
1 2
1 5
3 4
5 2
1 3
1 4
2 3
5 6

输出 #1复制

Yes
Yes
No

题解:

与第一个题不同之处是,在连接完m组上下级关系后,再判断p组的最高的领导人是否相同。

代码如下

#include"stdio.h"
int f[5009]={0},n,m,j;
void init()
{
	int i;
	for(i=1;i<=n;i++)
	f[i]=i;
	return;
}
int getf(int v)
{
	if(f[v]==v)
	return v;
	else
 	{
		f[v]=getf(f[v]);
		return f[v];	
	}	
} 
void merge(int v,int u)
{
	int t1,t2;
	t1=getf(v);
	t2=getf(u);
	if(t1!=t2)
	{
		f[t2]=t1;
	} 
	return; 
}
int main()
{
	int i,x,y;
	scanf("%d%d%d",&n,&m,&j);
	init(); 
	for(i=1;i<=m;i++)
	{
		scanf("%d%d",&x,&y);
		merge(x,y);
	}
	for(i=1;i<=j;i++)
	{
		scanf("%d%d",&x,&y);
		if(getf(x)==getf(y))
		printf("Yes\n");
		else printf("No\n");
	}
	return 0;
}

题目3:

题目描述

明天就是母亲节了,电脑组的小朋友们在忙碌的课业之余挖空心思想着该送什么礼物来表达自己的心意呢?听说在某个网站上有卖云朵的,小朋友们决定一同前往去看看这种神奇的商品,这个店里有 n 朵云,云朵已经被老板编号为 1,2,3,...,n,并且每朵云都有一个价值,但是商店的老板是个很奇怪的人,他会告诉你一些云朵要搭配起来买才卖,也就是说买一朵云则与这朵云有搭配的云都要买,电脑组的你觉得这礼物实在是太新奇了,但是你的钱是有限的,所以你肯定是想用现有的钱买到尽量多价值的云。

输入格式

第一行输入三个整数,n,m,w,表示有 n 朵云,m 个搭配和你现有的钱的数目。

第二行至 n+1 行,每行有两个整数, ci​,di​,表示第 i 朵云的价钱和价值。

第 n+2 至 n+1+m 行 ,每行有两个整数 ui​,vi​。表示买第 ui​ 朵云就必须买第 vi​ 朵云,同理,如果买第 vi​ 朵就必须买第 ui​ 朵。

输出格式

一行,表示可以获得的最大价值。

输入输出样例

输入 #1复制

5 3 10
3 10
3 10
3 10
5 100
10 1
1 3
3 2
4 2

输出 #1复制

1

说明/提示

  • 对于 30% 的数据,满足 1≤n≤100;
  • 对于 50% 的数据,满足 1≤n,w≤10^3,1≤m≤100;
  • 对于 100% 的数据,满足 1≤n≤10^4,0≤m≤5×10^3。

题解:

1.这个题目是并查集与01背包的集合,01背包要用一维数组,不然会内存超限。

2.买第 ui​ 朵云就必须买第 vi​ 朵云,同理,如果买第 vi​ 朵就必须买第 ui​ 朵。

即只要有上下级关系,买了其中一个要把整个集团的所有云买下来。

这样的话在最后判断一下所有最高领导人是否能够被买就可以了。

3.要把这个集团的价钱和价值集中在一起,只要在成为上下级的函数中把两个上下级的价钱和价值都叠加在上级就行了。

代码如下

#include"stdio.h" 
int n,m,t,f[10004],x1[10004],v[10004],w[10004];
void init()
{
	int i;
	for(i=1;i<=n;i++)
	f[i]=i;
}
int getf(int a)//找祖先 
{
	if(f[a]==a)
	return a;
	else
	{
		f[a]=getf(f[a]);
		return f[a];
	}
}
void merga(int a,int b)
{
	int a1,b1;
	a1=getf(a),b1=getf(b);
	if(a1!=b1)
	{
		f[b1]=a1;
		v[a1]+=v[b1];
		w[a1]+=w[b1];
	}
	return;
}
int max(int a,int b)
{
	if(a>b)
	return a;
	else
	return b;
}
int main()
{
	int x,y,j,g,sum1=0,sum2=0,sign=0;
	scanf("%d%d%d",&n,&m,&t);
	int i;
	init();
	for(i=1;i<=n;i++)
	scanf("%d%d",&v[i],&w[i]);
	for(i=1;i<=m;i++)
	{
	scanf("%d%d",&x,&y);
	merga(x,y);
	}
	for(i=1;i<=n;i++)
	{
	if(getf(i)==i)
	{
	for(j=t;j>=v[i];j--)
	{
	x1[j]=max(x1[j],x1[j-v[i]]+w[i]);
	}
	}
	}
	printf("%d",x1[t]);
	return 0;
}

题目4:

题目背景

小明在 A 公司工作,小红在 B 公司工作。

题目描述

这两个公司的员工有一个特点:一个公司的员工都是同性。

A 公司有 N 名员工,其中有 P 对朋友关系。B 公司有 M 名员工,其中有 Q 对朋友关系。朋友的朋友一定还是朋友。

每对朋友关系用两个整数(Xi​,Yi​) 组成,表示朋友的编号分别为 Xi​,Yi​。男人的编号是正数,女人的编号是负数。小明的编号是 1,小红的编号是 −1。

大家都知道,小明和小红是朋友,那么,请你写一个程序求出两公司之间,通过小明和小红认识的人最多一共能配成多少对情侣(包括他们自己)。

输入格式

输入的第一行,包含 4 个空格隔开的正整数 N,M,P,Q。

之后 P 行,每行两个正整数 Xi​,Yi​。

之后 Q 行,每行两个负整数 Xi​,Yi​。

输出格式

输出一行一个正整数,表示通过小明和小红认识的人最多一共能配成多少对情侣(包括他们自己)。

输入输出样例

输入 #1复制

4 3 4 2
1 1
1 2
2 3
1 3
-1 -2
-3 -3

输出 #1复制

2

说明/提示

对于 30% 的数据,N,M≤100,P,Q≤200;

对于 80% 的数据,N,M≤4×10^3,P,Q≤10^4;

对于 100% 的数据,N,M≤10^4,P,Q≤2×10^4。

题解:

1.这个题目就是求小明和小红所在两个集团中的人数最少集团的人数具体数据。

2.怎么求小明和小红所在两个集团的具体人数,只要在连接上下级完成后,遍历判断一下数组中与小明与小红的最高领导人相同的个数。

3.关于小红公司人下标的处理,用#include"math.h"头文件,取输入的数的绝对值+n就是小红公司人的下标。

代码如下

#include"stdio.h"
#include"math.h" 
int f[20009],n,m;
int getf(int a)//找祖先 
{
	if(f[a]==a)
	return a;
	else
	{
		f[a]=getf(f[a]);
		return f[a];
	}
}
void  merga(int a,int b)
{
	int a1,b1;
	a1=getf(a),b1=getf(b);
	if(a1!=b1)
	{
		f[b1]=a1;
	}
	return ;
}
int min(int a,int b)
{
	if(a>b)
	return b;
	else  return a;
}
int main()
{
	int p,q,i,x,y,ans1,ans2; 
	scanf("%d%d%d%d",&n,&m,&p,&q);
	for(i=1;i<=n+m;i++)
	f[i]=i;
	for(i=1;i<=p;i++)
	{
		scanf("%d%d",&x,&y);
		merga(x,y);
	}
	for(i=1;i<=q;i++)
	{
		scanf("%d%d",&x,&y);
		merga(n+abs(x),n+abs(y));
	}
	ans1=1; 
	for(i=2;i<=n;i++)
	{
		if(getf(i)==getf(1))
		ans1++;
	} 
	ans2=1;
	for(i=n+2;i<=n+m;i++)
	{
		if(getf(i)==getf(n+1))
		ans2++;
	}
	ans1=min(ans1,ans2);
	printf("%d",ans1);
	return 0;
}

题目5:

题目背景

A地区在地震过后,连接所有村庄的公路都造成了损坏而无法通车。政府派人修复这些公路。

题目描述

给出A地区的村庄数N,和公路数M,公路是双向的。并告诉你每条公路的连着哪两个村庄,并告诉你什么时候能修完这条公路。问最早什么时候任意两个村庄能够通车,即最早什么时候任意两条村庄都存在至少一条修复完成的道路(可以由多条公路连成一条道路)

输入格式

第1行两个正整数N,M

下面M行,每行3个正整数x,y,t,告诉你这条公路连着x,y两个村庄,在时间t时能修复完成这条公路。

输出格式

如果全部公路修复完毕仍然存在两个村庄无法通车,则输出−1,否则输出最早什么时候任意两个村庄能够通车。

输入输出样例

输入 #1复制

4 4
1 2 6
1 3 4
1 4 5
4 2 3

输出 #1复制

5

说明/提示

N≤1000,M≤100000

x≤N,y≤N,t≤100000

题解:

1.用一个结构体记录一下每一条公路的情况。用快排按照结构体time从小到大排序。

2.按time从小到大排好后的结构体,依次把结构体中的x,y连接上下级,连接一次判断一下集团数是否为1。如果是就输出当前结构体的time。如果到最后一个结构体集团数还不为1就输出-1。

代码如下

#include"stdio.h"
#include"stdlib.h"
int f[1009],n,m;
int getf(int a)//找祖先 
{
	if(f[a]==a)
	return a;
	else
	{
		f[a]=getf(f[a]);
		return f[a];
	}
}
void merag(int a,int b)//成亲戚 
{
	int a1,b1;
	a1=getf(a),b1=getf(b);
	if(a1!=b1)
	{
		f[b1]=a1;
	}
	return ;
}
struct node{
	int x;
	int y;
	int time;
}d[100009];
int com(const void *a,const void *b)
{
	return (*(struct node *)a).time>(*(struct node *)b).time?1:-1;
}
int main()
{
	int i,ans,j,t;
	scanf("%d%d",&n,&m);
	for(i=1;i<=n;i++)
	f[i]=i;
	for(i=0;i<m;i++)
	{
		scanf("%d%d%d",&d[i].x,&d[i].y,&d[i].time);
	}
	qsort(d,m,sizeof(d[0]),com);
	for(i=0;i<m;i++)
	{
		merag(d[i].x,d[i].y);
		ans=0;
		for(j=1;j<=n;j++)
		{
			if(f[j]==j)
			ans+=1;
		}
		if(ans==1)
		{
		printf("%d",d[i].time);
		break;
		}
		if(i==m-1&&ans!=1)
		printf("-1");
	}
	return 0;
}
举报

相关推荐

[并查集][C++]并查集模板

C++实现并查集

并查集入门(C++)

并查集__

并查集

java并查集

并查集模板

0 条评论