【并查集】团伙
[codevs2597] 团伙
时间限制: 1 s
空间限制: 128000 KB
题目等级 : 黄金 Gold
题目描述 Description
1920年的芝加哥,出现了一群强盗。如果两个强盗遇上了,那么他们要么是朋友,要么是敌人。而且有一点是肯定的,就是:
我朋友的朋友是我的朋友;
我敌人的敌人也是我的朋友。
两个强盗是同一团伙的条件是当且仅当他们是朋友。现在给你一些关于强盗们的信息,问你最多有多少个强盗团伙。
输入描述 Input Description
输入文件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是敌人。输入数据保证不会产生信息的矛盾。
输出描述 Output Description
输出文件gangs.out只有一行,表示最大可能的团伙数。
样例输入 Sample Input
6
4
E 1 4
F 3 5
F 4 6
E 1 2
样例输出 Sample Output
3
数据范围及提示 Data Size & Hint
2<=N<=1000
1<=M<=5000
1<=p q<=N
试题分析:这种问题我们一般有两种解法:①打标记 ②多个并查集
打标记这类方法会在银河英雄传说中看到
那么多个并查集如何解决呢?我们设1~N节点是记录朋友,N+1~2*N节点是记录敌人
既然两个人是朋友,根据题意,我们也要把他们的敌人合起来
如果两个人是敌人,那么merge(a,b+N),merge(a+N,b)
如何查询有几个团伙呢,就把所有人的根节点放进一个数组里面sort一下最后求下多少不同的就好了
代码:
#include<iostream> #include<cstring> #include<cstdio> #include<queue> #include<stack> #include<vector> #include<algorithm> //#include<cmath> using namespace std; const int INF = 9999999; #define LL long long inline int read(){ int x=0,f=1;char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } int fa[3001]; int a[3001]; int N,M; int ans; void init(){ for(int i=1;i<=2*N;i++) fa[i]=i; } int find(int x){ if(x!=fa[x]) return fa[x]=find(fa[x]); return x; } void merge(int a,int b){ int x=find(a),y=find(b); if(x==y) return ; fa[y]=x; return ; } int main(){ //freopen(".in","r",stdin); //freopen(".out","w",stdout); N=read(),M=read(); init(); for(int i=1;i<=M;i++){ char c;cin>>c; int a=read(),b=read(); if(c=='F'){ merge(a,b); } else{ merge(a+N,b); merge(a,b+N); } } for(int i=1;i<=N;i++) a[i]=find(i); sort(a+1,a+N+1); for(int i=1;i<=N;i++) if(a[i]!=a[i-1]) ans++; printf("%d\n",ans); return 0; }
你——悟到了么?