并查集

输入输出格式
先来一段不严谨的描述,这是看了啊哈算法之后的回忆
比如说现在有一些贼(1号,2号,3,号,…,n号)
他们之间又如下关系
1 3(1号和3号一伙)
2 4
2 3

让你求出一共有几个独立的团伙
我们可以这样,每得到一条边(u,v),我们就把u所在的集合与v所在的集合合并,当然可以写程序模拟这个过程,使用动态分配的数组这并不难,但是现在有更好的做法:
搞一个数组,下标是贼,值是下标贼所在团体的头头
好,一开始很简单

123456
123456

现在读到一条关系比如说1 2
一号和二号是一伙的,我们说1号与2号在以1为首的团伙里,也可以说1号与2号在以2为首的团伙里,这两种说法是等价的,我们规定右边的牛逼一点,所以1号要服从2号

223456
123456

现在又读到一条2 5,那么2号要服从5号

253456
123456

可1号怎么办?一号是不是也要改成5号呢?不用,这里我们规定,贼所在的团伙就是以那个团伙的boss为代表的集合,什么样的人才算boss?f[i]=i的人才是boss,就像这里f[5]=5,f[2]=5,f[1]=2,我们可以递归查询,直到找到boss为止

def pre(i):
	if(f[i]==i)return i;
	esle return pre(f[i]);

所以对于一个贼i,它的boss pre(i)就是我们在这个问题里最关心的值所以,只要改动了f[pre(i)],他的所有小弟都跟着改动
比如这个时候我们就可以用f[pre(2)]=f[pre(5)];
这样2所在的集合就与5所在的集合合并了,因为2号团伙的boss归顺了5号团伙的boss。
1号的爸爸是2号,2号的爸爸是5号,5号才算boos,所以1号也算5号的人


现在用数学一点的写法来描述
把在本题中我们要操作的对象视为以某个元素为代表的集合
于是我们有了一个数组

int f[Max + 1];

在合并集合问题中一开始把所有的元素视为以自己为代表元素的集合

for (int i = 1; i <= n; i++){
		f[i] = i;//我为我自己代言
	}
//以后可能f[2]=1,合并2号元素与1号元素,这样f[2]==1,f[1]==1,那就说明1号元素与2号元素在同一个集合内

如何查找某个元素所在集合的代表元素的下标呢?
自己为自己代言的就是代表元素,否者就是其他集合的元素

def pre(i):
	if(f[i]==i)return i;
	esle return pre(f[i]);

然后读入一条边u,v,让(u所在集合的代表元素)变成(v所在集合的代表元素)

while(1):
	u=read(),v=read();
	f[pre(u)]=f[pre(v)]

这样以后查询(u所在集合).任意一个元素.代表元素都可以得到(v所在集合).代表元素
这也就意味着v集合和u集合合并了,因为他们共享同一个代表元素


#include <stdio.h>
#include <stdlib.h>
#define Max 200000
int f[Max + 1];
int read() {
	int x = 0, f = 1; char c = getchar();
	while (c<'0' || c>'9') { if (c == '-')f = -1; c = getchar(); }
	while (c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
	return x * f;
}
int pre(int I) {
	if (f[I] == I)return I;//如果自己是自己的老大那么说明找到了大Boss(以自己为代表的集合)
	else return f[I] = pre(f[I]);//否者的话去问下这个爸爸自己的老大是谁
}

int main()
{
	int n=read(), m=read();
	//初始化f
	for (int i = 1; i <= n; i++){
		f[i] = i;//把每个人的老大设置为自己
	}
	//合并以及输出
	//这个输入既有查询又有合并
	register int z, x, y,preX,preY;
	for (int i = 1; i <= m; i++){
		z = read(), x = read(), y = read();
		preX = pre(x); preY = pre(y);
		if (z == 1) {//Z等于1代表合并
			f[preX]= f[preY];//把左边的老大赋值为右边的老大,这样就完成了两个集合的合并
		}else {
			if (f[preX] == f[preY])printf("Y\n");//看一下老大是不是一样的
			else printf("N\n");
		}
	}
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值