【题目描述】
1920年的芝加哥,出现了一群强盗。如果两个强盗遇上了,那么他们要么是朋友,要么是敌人。而且有一点是肯定的,就是:
我朋友的朋友是我的朋友。
我敌人的敌人也是我的朋友。
两个强盗是同一团伙的条件是当且仅当他们是朋友。现在给你一些关于强盗们的信息,问你最多有多少个强盗团伙。
【输入格式】
输入的第一行是一个整数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是敌人。输入数据保证不会产生信息的矛盾。
【输出格式】
输出只有一行,表示最大可能的团伙数。
【输入样例】
6
4
E 1 4
F 3 5
F 4 6
E 1 2
【输出样例】
3
汪维正说这个题是并查集的经典例题。但是我一直没有仔细研究过并查集。只有在用kruscal时才会小小地用一下。今天我决定仔细弄一下并查集。
首先,如果两人是朋友,那么就把两人合并。
除此之外,我们再维护一个e[i],表示i的一个敌人。如果两人是敌人,那么如果e[i]为空,就更新e[i],否则,就把e[i]和j合并。根据敌人的敌人是朋友的原则,如果j和i是敌人,那么j同e[i]则是朋友,所以合并。同样的,对于i和e[j],也是如此。
最后统计一下根结点就行了。
Code:
#include
#include
#include
#include
using namespace std;
int n,m;
const int maxn=1000;
int f[maxn+10],e[maxn+10];
int find(int x)
{
if (f[x]==x)
return f[x];
f[x]=find(f[x]);
return
f[x];
}
void together(int a,int b)
{
int aa=find(a),bb=find(b);
f[aa]=f[bb];
}
int main()
{
freopen("577.in","r",stdin);
freopen("577.out","w",stdout);
scanf("%d%d",&n,&m);
for (int
i=1; i<=n; i++)
f[i]=i;
while
(m--)
{
char flag=(getchar(),getchar());
int a,b;
scanf("%d%d",&a,&b);
if (flag=='F') together(a,b);
else
{
if (!e[a]) e[a]=b;
else together(e[a],b);
if (!e[b]) e[b]=a;
else together(e[b],a);
}
}
int
ans=0;
for (int
i=1; i<=n; i++)
if (f[i]==i) ans++;
printf("%d\n",ans);
fclose(stdin);
fclose(stdout);
return 0;
}
成绩: