P1892 [BOI2003]团伙
题目描述
1920年的芝加哥,出现了一群强盗。如果两个强盗遇上了,那么他们要么是朋友,要么是敌人。而且有一点是肯定的,就是:
我朋友的朋友是我的朋友;
我敌人的敌人也是我的朋友。
两个强盗是同一团伙的条件是当且仅当他们是朋友。现在给你一些关于强盗们的信息,问你最多有多少个强盗团伙。
输入输出格式
输入格式:
输入文件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只有一行,表示最大可能的团伙数。
输入输出样例
输出样例#1:
3
先说一下思路:
对于只有朋友信息的数据就不用说了。对于敌人的信息,我们可以给每一个人i设置一个敌人集合E[i],当输入i和j时,我们就把i和E[j]合并,把j和E[i]合并。
值得一提的是,要注意空集的处理,所谓空集就是一开始的时候,每个人都没有敌人。
昨天晚上做了一晚上卡在了50分,今天又接着来啃,问同学,同学说我的想法很对,叫我自己慢慢调(WCNM我要是调的出来就不找你了)
没办法,慢慢找喽。
先给大家看看我的50分的代码。
#include <iostream>
#include <cstdio>
using namespace std;
int n, m, Ans, x, y;
int f[1008], E[1008];
char C;
bool vis[1008];
int find(int x) {
if(x == f[x]) return f[x];
else return f[x] = find(f[x]);
}
int main() {
scanf("%d%d", &n, &m);
for(int i=1; i<=n; i++) f[i] = i;
for(int i=1; i<=m; i++) {
cin>>C>>x>>y;
int xx, yy;
if(C == 'E') {
if(E[x] != 0) {
f[y] = find(E[x]);
}
if(E[y] != 0) {
f[x] = find(E[y]);
}
E[x] = y, E[y] = x;
}
if(C == 'F') {
f[x] = find(y);
}
}
for(int i=1; i<=n; i++) {
if(!vis[find(i)]) {
vis[find(i)] = 1;
Ans++;
}
}
printf("%d", Ans);
}
不知道大家看出什么问题了没。
没看出的同学可要小心了,
看一下我的合并集合时候的操作,是不是把之前合并好的集合都打乱了。
为什么?因为我合并时修改的是f[x]的值,而不是f[find(x)]的值,这就会导致很可怕的错误。
往后的此类操作都是这样的。
好了,说到这里该放上AC的代码了
#include <iostream>
#include <cstdio>
using namespace std;
int n, m, Ans, x, y;
int f[1008], E[1008];
char C;
bool vis[1008];
int find(int x) {
if(x == f[x]) return x;
else return f[x] = find(f[x]);
}
int main() {
scanf("%d%d", &n, &m);
for(int i=1; i<=n; i++) f[i] = i;
for(int i=1; i<=m; i++) {
cin>>C>>x>>y;
if(C == 'E') {
if(E[x] != 0) {
f[find(y)] = find(E[x]);
}
else E[x] = find(y);
if(E[y] != 0) {
f[find(x)] = find(E[y]);
}
else E[y] = find(x);
}
if(C == 'F') {
f[find(x)] = find(y);
}
}
for(int i=1; i<=n; i++) {
if(!vis[find(i)]) {
vis[find(i)] = 1;
Ans++;
}
}
printf("%d", Ans);
}