题目描述
1920年的芝加哥,出现了一群强盗。如果两个强盗遇上了,那么他们要么是朋友,要么是敌人。而且有一点是肯定的,就是:
我朋友的朋友是我的朋友;
我敌人的敌人也是我的朋友。
两个强盗是同一团伙的条件是当且仅当他们是朋友。现在给你一些关于强盗们的信息,问你最多有多少个强盗团伙。
Input/Output 格式 & 样例
输入格式
输入文件gangs.in的第一行是一个整数N(2<=N<=1000),表示强盗的个数(从1编号到N)。
第二行M(1<=M<=5000),表示关于强盗的信息条数。
以下M行,每行可能是F p q
或是E p q
(1<=p q<=N),F表示p和q是朋友,E表示p和q是敌人。
输入数据保证不会产生信息的矛盾。
输出格式
输出文件gangs.out只有一行,表示最大可能的团伙数。
输入样例
6
4
E 1 4
F 3 5
F 4 6
E 1 2
输出样例
3
解题思路
很显然这是一道并查集的题目
初始时我们把每一个人单独列为一个团伙
由题可得,这道题主要有如下合并方式:
- 我的朋友是我的朋友
- 我的朋友的朋友是我的朋友
- 我的敌人的朋友是我的敌人
- 我的敌人的敌人是我的朋友
那么我们要另开一个\(Enemy[\ ]\)数组,\(Enemy[i]\)表示 \(i\) 的其中一个敌人
每次合并敌人的时候,先判断是否有记录过敌人:
- 如果有,那么就把当前的敌人和记录的敌人合并在一个团伙里
- 如果没有,那么就把当前的敌人记录
最后开一个数组\(count[\ ]\)进行统计
- 开始时并查集数组要开两倍,因为你要把敌人和朋友存在一个数组里
- 合并敌人时要注意合并的不是敌人本身,而是\(Find(\)敌人\()\)
- 最后统计的时候也要统计\(Find(\)敌人\()\)
对了,注意输入...
建议使用iostream...
别问我为什么会写上这句话
代码实现
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cctype>
using namespace std;
const int MAXN = 1000 + 10;
int U[MAXN * 2], Enemy[MAXN * 2], n, m;
int count[MAXN * 2], cnt;
inline int getint() {
int s = 0, x = 1;
char ch = getchar();
while (!isdigit(ch)) {
if (ch == '-') x = -1;
ch = getchar();
}
while (isdigit(ch)) {
s = s * 10 + ch - '0';
ch = getchar();
}
return s * x;
}
inline void putint(int x) {
if (x < 0) {
x = -x;
}
if (x >= 10) {
putint(x / 10);
}
putchar(x % 10 + '0');
}
int Find(int x) {
if (U[x] == x) return x;
return U[x] = Find(U[x]);
}
void Union(int x, int y) {
x = Find(x), y = Find(y);
if (x == y) return;
U[x] = y;
return;
}
int main(int argc, char *const argv[]) {
freopen("P1892.in", "r", stdin);
cin >> n >> m;
for (int i = 0; i <= n * 2; ++i) U[i] = i;
for (int i = 1; i <= m; ++i) {
char c;
int x, y;
cin >> c >> x >> y;
switch(c) {
case 'F': {
Union(x, y);
break;
}
case 'E': {
if (Enemy[x] == 0) Enemy[x] = Find(y);
else Union(y, Enemy[x]);
if (Enemy[y] == 0) Enemy[y] = Find(x);
else Union(x, Enemy[y]);
}
}
}
for (int i = 1; i <= n; ++i) ++count[Find(i)];
for (int i = 1; i <= n; ++i) if (count[i]) ++cnt;
printf("%d\n", cnt);
return 0;
}