P1892 [BalticOI 2003] 团伙
题目描述
现在有 nnn 个人,他们之间有两种关系:朋友和敌人。我们知道:
- 一个人的朋友的朋友是朋友
- 一个人的敌人的敌人是朋友
现在要对这些人进行组团。两个人在一个团体内当且仅当这两个人是朋友。请求出这些人中最多可能有的团体数。
输入格式
第一行输入一个整数 nnn 代表人数。
第二行输入一个整数 mmm 表示接下来要列出 mmm 个关系。
接下来 mmm 行,每行一个字符 optoptopt 和两个整数 p,qp,qp,q,分别代表关系(朋友或敌人),有关系的两个人之中的第一个人和第二个人。其中 optoptopt 有两种可能:
- 如果 optoptopt 为
F
,则表明 ppp 和 qqq 是朋友。 - 如果 optoptopt 为
E
,则表明 ppp 和 qqq 是敌人。
输出格式
一行一个整数代表最多的团体数。
输入输出样例 #1
输入 #1
6
4
E 1 4
F 3 5
F 4 6
E 1 2
输出 #1
3
说明/提示
对于 100%100\%100% 的数据,2≤n≤10002 \le n \le 10002≤n≤1000,1≤m≤50001 \le m \le 50001≤m≤5000,1≤p,q≤n1 \le p,q \le n1≤p,q≤n。
题解
#include<iostream>
using namespace std;
int n, m, p, q;
char opt;
int a[1005]; //并查集的父节点存储
int b[1005]; //存储每个人的敌人
//查找、路径压缩
inline int find(int x) {
if (x == a[x]) return x;
return a[x] = find(a[x]);
}
//合并
inline void join(int x, int y) {
x = find(x), y = find(y);
if (x != y)
a[x] = y;
}
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++)
a[i] = i;
while (m--) {
cin >> opt >> p >> q;
if (opt == 'F') {
join(p, q);
}
else {
if (!b[p]) b[p] = q;//标记为敌人
else join(b[p], q);//敌人的敌人就是朋友
if (!b[q]) b[q] = p;
else join(b[q], p);
}
}
int count = 0;
for (int i = 1; i <= n; i++) {
if (find(i) == i) {
count++;
}
}
cout << count;
return 0;
}
代码的主要功能解析:
-
数据结构:
- a[] 数组用于并查集的父节点存储
- b[] 数组用于存储每个人的敌人
-
核心函数:
- find(x):查找元素 x 的根节点,并进行路径压缩优化
- join(x, y):将 x 和 y 合并到同一个集合中(表示他们是朋友)
-
主逻辑:
- 初始化:每个人都是自己的朋友(a[i] = i)
- 处理 m 条关系指令:
‘F’ 表示 p 和 q 是朋友,直接合并
其他字符(通常是 ‘E’ 表示敌人):
如果 p 没有敌人,则记录 q 为 p 的敌人
如果 p 已有敌人,则 p 的敌人与 q 成为朋友(敌人的敌人是朋友)
对 q 执行同样的操作
-
结果计算:
- 统计有多少个独立的朋友圈(根节点的数量)