并差集的用途:在处理一个数列有多个两两相互关联的元素,将多个相互关联的放入一个集合,查找并输出有多少组集合。
思路:用一个一维数组,将每个元素存入一个数组内。在通过元素,下标的联系构建一个集合。
想法就是,如果n个人,每个人都有一个人编号1~N。
第一: 将数组的下标和储存内容一致,就是用一个循环for(i=1;i<=n;i++) s[i]=i;//这时的还没有输入有关系的元素,所以这个
代表着每个元素就是一个集合,所以集合的标志为s[i]=i,就是下标和储存内弄一致。
第二: 将两个有关系的元素x,y进行一个集合的构建。构建集合的方法就是将下标和储存内容结合,在用一个标志代表集合就行了。s[x]=y,s[y]=y,这是构建了一个集合那只有两个元素的集合,集合的标志就是s[y]=y。这个标志就是集合的标志。
第三: 将集合与集合进行一个组合,组合方法与元素与元素之间的组合一样,就是将两个标志s[x]=y,s[y]=y。改变为s[x]=y,
s[y]=y这就是集合与集合之间的构建集合的方式。
第四: 将数组内的标志数量有多少就可以知道有多少集合了。将数量输出就行了。
时刻注意:集合的标志就是元素的下标与储存的内容一致。
问题描述:
今天是伊格那丢的生日。他邀请了很多朋友。现在是吃晚餐的时间。伊格内修斯想知道他至少需要多少张桌子。你必须注意到,不是所有的朋友都认识对方,而且所有的朋友都不想和陌生人在一起。
这个问题的一个重要的规则是,如果我告诉你A知道B, B知道C,那意味着A B C知道彼此,所以他们可以呆在一张桌子上。
例如:如果我告诉你A知道B, B知道C, D知道E,所以A, B, C可以放在一个表中,D, E必须留在另一个表中。所以伊格那丢至少需要两张桌子。
输入
输入从一个整数T(1<=T<=25)开始,它表示测试用例的数量。然后T测试用例。每个测试用例以两个整数N和M(1<=N,M<=1000)开始。N表示朋友的数量,朋友的标记从1到N,然后M行跟随。每一行包含两个整数A和B(A!=B),这意味着朋友A和朋友B彼此认识。在两个案例之间将会有一个空白行。
2
5 3
1 2
2 3
4 5
5 1
2 5
5 3
1 2
2 3
4 5
5 1
2 5
输出
对于每个测试用例,只需输出Ignatius至少需要多少表。不要打印任何空格。
2
4
4
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
int t, n, m, sa, sb; int s[1000];//数组的作用
int findset(int x) {//函数的作用:是查找元素在哪个集合内
while (s[x] != x) { x = s[x]; }
return x;//x为一个集合的标志
}
void set() {//执行的第一步,集合的初始化
for (int i = 1; i <= n; i++)
{
s[i] = i;
}
}
void unionset(int a, int b)
{
if (a != b) {//判断是不是一个数组
s[a] = b;
}
}int main() {
int a, b, k, i; int r[1000];
scanf("%d", &t);//t为测试数据
while (t--) {
k = 0; scanf("%d%d", &n, &m);//N为人数,m为有多少组有关系的元素组
set();//执行的第一步,在上面明文详解
memset(r, 0, sizeof(r));//这个数组是为了表明有没有重复的数组被找到然后
while (m--) {
scanf("%d%d", &a, &b);
sa = findset(a);
sb = findset(b);
unionset(sa, sb);//组合为一个集合
}
for (i = 1; i <= n; i++) {
if (r[findset(i)] == 0){//防止在找到标志之后出现的多次加加的现象
k++; r[findset(i)]++;
}
} printf("%d\n", k);//输出多少组
}
return 0;
}