敌人的敌人即朋友——种类并查集
题目:关押罪犯https://www.luogu.com.cn/problem/P1525
在基础并查集中,我们使用朋友的朋友还是朋友这一判断方式来合并不同的单位。但是在生活中还有一种状态:敌人的敌人即是朋友。我们将这种并查集成为种类并查集。
和基础并查集有很相似,但是 多了一个判断:2个元素是否属于同一个集团(不是集合, 集合是用来判断2个元素是否能够判断他们属不属于同一个集团。)
举个例子, 假如知道1和2在不同的集合, 3和4在不同集合,我们就不能判断1和3是否属于一个集合。
而集团是可以判断他们是否在同一个集团假如:已知1和2在不同集团,2和3在不同集团, 那么我们就知道1和3在同一个集团);
以这道题为例:
这道题人与人之间有两种关系
为此我们需要两个数组来存储数据。设a[]数组代表着朋友关系(甲和乙没有矛盾,a[i]代表i号的朋友),b[]数组代表敌人关系(b[i]代表i号的敌人)。
首先将a[]数组初始化,自己是自己的朋友。b数组取空(假定一开始所有人都是朋友,输入生成敌人关系)
每读入一组数据,加入一组仇人关系: 甲(i) 乙(j) 仇恨值(v)
首先判断甲乙是不是在一个监狱里面,条件成立则直接输出仇恨值。否则进入以下操作
假如b[i]为空的话,将b[i]的值改成当前仇人乙的代号j
b[i]不为空的话,就在a数组将b[i]中记录本来存在的仇人编号与新仇人标号合并,:首先找到b[i]号最开始的朋友与j号最开始的朋友,让他们存在于一个团体。
对乙做同样的操作
#include <cstdio>
#include <algorithm>
using namespace std;
struct data {int x,y,z;};//结构体便于排序的变换
data f[100005];
int n,m,a[20005],b[20005],i;
inline bool cmp(data a,data b)//重写cmp函数
{
return a.z>b.z;
}
inline int find(int x)
{
if(a[x]==x) return x;
a[x]=find(a[x]);
return a[x];
}
inline void ad(int x,int y)//合并
{
x=find(a[x]);
y=find(a[y]);
a[x]=y;
}
inline bool check(int x,int y)//查找
{
x=find(x);
y=find(y);
if(x==y) return true;
return false;
}
int main()
{
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++) a[i]=i;
for(i=1;i<=m;i++)
scanf("%d%d%d",&f[i].x,&f[i].y,&f[i].z);
sort(f+1,f+m+1,cmp);
for(i=1;i<=m+1;i++)//为什么m+1呢?如果运行到m+1会输出0,想想为什么。
{
if(check(f[i].x,f[i].y)) {printf("%d",f[i].z);break;}//如果两个罪犯已经在同一监狱就输出 ,并退出
else
{
if(!b[f[i].x]) b[f[i].x]=f[i].y;//标记“敌人”
else {ad(b[f[i].x],f[i].y);}//将敌人的敌人合并
if(!b[f[i].y]) b[f[i].y]=f[i].x;
else {ad(b[f[i].y],f[i].x);}
}
}
return 0;
}

浙公网安备 33010602011771号