九度 1526:朋友圈(并查集)
题目描述:
假如已知有n个人和m对好友关系(存于数字r)。如果两个人是直接或间接的好友(好友的好友的好友...),则认为他们属于同一个朋友圈,请写程序求出这n个人里一共有多少个朋友圈。
假如:n = 5 , m = 3 , r = {{1 , 2} , {2 , 3} , {4 , 5}},表示有5个人,1和2是好友,2和3是好友,4和5是好友,则1、2、3属于一个朋友圈,4、5属于另一个朋友圈,结果为2个朋友圈。
思路
1. 裸的并查集, 回顾下并查集的知识
2. 并查集主要有两个操作, 一个是 merge, 一个是 find. find 用于找到一个集合的标志, merge 用于合并两个集合. 在实际的操作中, 我们只要用 merge 就好了, find 不直接使用
3. find 又有路径压缩的说法, 这是在寻找集合标志的过程中, 使得这条路径上的所有节点到标志的距离都压缩成 1, 使用递归完成
4. 比较来了两个节点 0 1, 我们要做的就是 merge(0,1), 将 0, 1 合并到一个集合中. merge 的实际操作是将两个集合的标志建立父子关系
代码
#include <iostream> #include <stdio.h> using namespace std; int father[100010]; int find(int x) { if(father[x] != x) { father[x] = find(father[x]); } return father[x]; } void merge(int a, int b) { int fa = find(a); int fb = find(b); if(fa == fb) return; father[fa] = fb; } int main() { //freopen("testcase.txt", "r", stdin); int n, m; while(scanf("%d", &n) && n != 0) { scanf("%d", &m); for(int i = 1; i <= n; i ++) father[i] = i; int a, b; for(int i = 0; i < m; i ++) { scanf("%d%d", &a, &b); merge(a,b); } int cnt = 0; for(int i = 1; i <= n; i ++) { if(father[i] == i) cnt++; } cout << cnt << endl; } return 0; }