淳朴的并查集,思路也很简单~
思路:如果是两个人是好盆友,很自然就要将他们所在集合合并了,如果不是朋友比如x—y 是敌人
1.那么就将x与y的其他敌人合并,why?因为敌人的敌人就是朋友了(他们有共同的敌人~)
2.除此之外还要将y与x的其他敌人合并,why?下面就来解释这个问题~
比如上面的样例~假如我们不执行上面的第二点
E 1 4 将4与所有1的其他敌人合并,很显然没有。
F 3 5 合并 3 5
F 4 6 合并4 6
E 1 2 将2与所有1的其他敌人合并,也就是将4的祖先和2的祖先合并
合并好以后三个犯罪团伙,符合题意~
6
4
E 1 4
F 3 5
F 4 6
E 2 1
我们把样例的最后一个1 2 改成2 1,还是按照上第一个来操作的话
将1与2的其他敌人合并
很显然,这里不能合并~
最终会有4个团伙,所以我们还要将2与1的其他敌人合并~
下面是AC代码~
#include <iostream>
#include <vector>
#include <set>
#define Max 1005
using namespace std;
int par[Max];
vector<int > a[Max]; //a[x] 表示x和a[x]里面是敌人关系
int find_par(int x1); //寻找祖先节点
void unions(int x1,int x2); //将x1 x2的祖先合并 天下一家亲~
set<int> ss;
int main()
{
int N,M,p,q;
char x;
cin>>N>>M;
for(int i=1;i<=N;i++)
{
par[i]=i;
}
for(int i=1;i<=M;i++)
{
cin>>x;
cin>>p>>q;
if(x=='F')
{
unions(p,q);
}
else
{
for(int j=0;j<a[p].size();j++)
{
unions(a[p][j],q);
}
for(int j=0;j<a[q].size();j++)
{
unions(p,a[q][j]);
}
a[p].push_back(q);
a[q].push_back(p);
}
}
for(int i=1;i<=N;i++)
{
ss.insert(find_par(i));
}
cout<<ss.size()<<endl;
return 0;
}
int find_par(int x1) //寻找祖先节点
{
return x1==par[x1] ? x1 :par[x1]=find_par(par[x1]);//路径压缩
}
void unions(int x1,int x2) //将x1 x2的祖先合并 天下一家亲~
{
par[find_par(x1)]=find_par(x2);
}