并查集-团伙
题目描述
在某城市里住着 n 个人,任何两个认识的人不是朋友就是敌人,而且满足: 1、我朋友的朋友是我的朋友; 2、我敌人的敌人是我的朋友; 所有是朋友的人组成一个团伙。告诉你关于这 n 个人的 m 条信息,即某两个人是朋友, 或者某两个人是敌人,请你编写一个程序,计算出这个城市多可能有多少个团伙?
输入
第 1 行为 n 和 m,1<n<1000,1<=m<=100 000; 以下 m 行,每行为 p x y,p 的值为 0 或 1,p 为 0 时,表示 x 和 y 是朋友,p 为 1 时, 表示 x 和 y 是敌人。
输出
一个整数,表示这 n 个人多可能有几个团伙。
样例输入
6 4
1 1 4
0 3 5
0 4 6
1 1 2
样例输出
3
- 提示
- 采用图的形式来存储每个点的敌人
代码
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1100,M=N*N;
int n,m;
int pa[N]; // 父亲数组
int h[N],e[M],ne[M],idx = 0; // 领接链表存储每个点的敌人
// 输入领接链表
int add(int a,int b){
e[idx] = b;
ne[idx] = h[a];
h[a] = idx++;
}
// 并查集 find 方法 : 一直递归找他的父亲 最后将x直接指向最高层的父亲
int find(int x){
if(pa[x] != x) pa[x] = find(pa[x]);
return pa[x];
}
int main(){
// 初始化领接链表的头数组
memset(h,-1,sizeof h);
scanf("%d %d",&n,&m);
// 初始化父亲数组 将每个元素的父亲初始化为他自己
for(int i = 1; i <= n;i++){
pa[i] = i;
}
for(int i = 1; i <= m;i++){
int p,x,v;
scanf("%d %d %d",&p,&x,&v);
int fx = find(x);
int fy = find(v);
// 如果是朋友就合并成一个集合
if(p == 0){
pa[fx] = fy;
}else{
// 遍历x 的敌人 他的敌人插入到 v 的集合中
for(int j = h[x];j != -1;j = ne[j]){
int k = e[j];
int fk = find(k);
pa[fk] = fy;
}
for(int j = h[v];j != -1;j = ne[j]){
int k = e[j];
int fk = find(k);
pa[fk] = fx;
}
// 将v存为x敌人
add(x,v);
add(v,x);
}
}
int sum = 0;
for(int i = 1; i <= n;i++){
if(find(i) == i){
sum++;
}
}
cout << sum << endl;
}