线上OJ:
核心思想:
1、朋友的朋友就是朋友
。这句话意味着朋友之间直接合并。
2、敌人的敌人就是朋友
。这句话说明,如果 a 和 b 是敌人,a 和 c 也是敌人,则 c 和 b 就是朋友。如果 a 和 d 也是敌人,则 d 和 {c, b} 都是朋友。
2.1 所以当 a 和 x 是敌人时,把 x
和 a 的敌人
合并即可。用一个数组 a[i] 记录第 i 个人的敌人(只需记录一个即可,因为合并时会合并他们的根节点)。
2.2 如果 a 和 x 是敌人,但 a[i] 数组为空,则把 x 赋值给 a[i],作为它的第一个敌人。
3、最后判断有几个团伙时,只要数下有几个根即可(p[i]==i 的为根)。
题解代码:
#include <bits/stdc++.h>
using namespace std;
int p[1010], enemy[1010] = {0}, n, m, x, y, k;
int find(int x) // 查询x所在集合中的根节点
{
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
void unionn(int x, int y) // 合并x和y
{
int a = find(x), b = find(y);
if (a != b) p[a] = b;
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i ++ ) p[i] = i; // 初始化每个元素的根节点为自己
for (int i = 1; i <= m; i ++ )
{
scanf("%d%d%d", &k, &x, &y);
if (k == 0)
unionn(x, y); // 如果两个人是朋友,直接合并
else if (k == 1)
{
if(!enemy[x])
enemy[x] = y; // 如果x还没有敌人,则赋值y为x的敌人
else
unionn(enemy[x], y); // 如果x已经有敌人,则把y合并到x的敌人群里(敌人的敌人就是朋友)
if(!enemy[y]) // 反之亦然
enemy[y] = x;
else
unionn(enemy[y], x);
}
}
int ans = 0;
for(int i = 1; i <= n; i++)
if(p[i] == i) ans++; // 满足p[i] == i的说明是根。根的数量就要是团伙的数量
printf("%d\n", ans);
}