题目链接:
https://www.luogu.com.cn/problem/P1892https://www.luogu.com.cn/problem/P1892
题目:
现在有 n 个人,他们之间有两种关系:朋友和敌人。我们知道:
- 一个人的朋友的朋友是朋友
- 一个人的敌人的敌人是朋友
现在要对这些人进行组团。两个人在一个团体内当且仅当这两个人是朋友。请求出这些人中最多可能有的团体数。
输入格式:
第一行输入一个整数 n 代表人数。
第二行输入一个整数 m 表示接下来要列出 m 个关系。
接下来 m 行,每行一个字符 opt 和两个整数 p,q,分别代表关系(朋友或敌人),有关系的两个人之中的第一个人和第二个人。其中 opt 有两种可能:
- 如果 opt 为
F
,则表明 p 和 q 是朋友。 - 如果 opt 为
E
,则表明 p 和 q 是敌人。
输出格式:
一行一个整数代表最多的团体数。
例:
输入:
6 4 E 1 4 F 3 5 F 4 6 E 1 2
输出:
3
数据提示:
对于 100% 的数据,2 ≤ n ≤ 1000,1 ≤ m ≤ 5000,1 ≤ p , q ≤ n
思路:
首先看题,可以得知需要求得最多的团体数。看到团体/集合等词语时,我们应该反应过来考查的是并查集。那么我们可以拿出并查集模板了:
class Union {
public:
Union(int n) :fa(n + 1) {
for (int i = 0; i < n; i++) {
fa[i] = i;
}
}
int find(int i) {
return fa[i] = (fa[i] == i ? i : find(fa[i]));
}
void merge(int x, int y) {
if (find(x) == find(y)) {
return;
}
fa[find(x)] = find(y);
return;
}
vector<int> fa;
};
首先用俩个整形n和num用于接收有多少个人和多少种关系。
接着循环num次,用op表示关系,per和tar表示俩人来接收每次都关系。(洛谷题目输入有问题,使用scanf有时会出现‘\n‘,‘\0‘等)。
因为在关系中存在朋友关系和敌对关系,但是敌人的敌人是朋友,所以这里要使用到反集的知识(没做前,我也不知道反集这东西)。
(不懂的看看这里 -> 并查集反集及应用-CSDN博客 )
对于朋友关系,在并查集中直接连通俩人即可。
对于敌人关系,我们让per与n+tar相连通,tar与n+per相连通即可。(因为敌人的敌人是朋友,我们让tar与n+per相连通,当tar的其他敌人与n+per相连通时,就实现了敌人的敌人是朋友)
然后,使用循环,判断该人的父结点是否是自己,若是,则ans加一,反正不变。
最后输出ans即可。
AC代码:
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<vector>
using namespace std;
class Union {
public:
Union(int n) :fa(n + 1) {
for (int i = 0; i < n; i++) {
fa[i] = i;
}
}
int find(int i) {
return fa[i] = (fa[i] == i ? i : find(fa[i]));
}
void merge(int x, int y) {
if (find(x) == find(y)) {
return;
}
fa[find(x)] = find(y);
return;
}
vector<int> fa;
};
int n, num, per, tar;
char op;
int main() {
scanf("%d", &n);
Union u(2 * n);
scanf("%d", &num);
getchar();
while (num--) {
cin >> op >> per >> tar;
if (op == 'E') {
u.merge(n + per, tar);
u.merge(n + tar, per);
}
else if (op == 'F') {
u.merge(per, tar);
}
}
int ans = 0;
for (int i = 1; i <= n; i++) {
if (u.find(i) == i) {
ans++;
}
}
printf("%d\n", ans);
return 0;
}
如果我的文章对你有所帮助,不妨给我个关注何点赞吧。