这是一道进阶的并查集的题目,其做法有两种,第一种是带权并查集,新开一个数组维护每个节点和根节点的关系,但由于博主太菜,这个做法研究了一下午也没有搞懂,所以就采用了我认为更好理解一点的扩展域并查集,这道题中要求输出的最大冲突值最小,所以贪心的来做,我们先按冲突值从大到小排一个序,尽量不让冲突值大的事件发生,题目中说共有两个监狱,有m对罪犯之间有冲突,只要这两个罪犯关在了同一间监狱里,这个冲突就会发生,所以从冲突值最大开始,依次将两个罪犯往不同的两个监狱里放,如果到哪一步放不进去了,当前这个冲突值即为答案。
那么问题来了,我们怎么样应用扩展域并查集维护这样一对关系呢,我们将fa开到2*n,其中[1,n]维护的是与自己在一个监狱里的人的集合,[n+1,2n]维护的是我的敌人所在监狱的集合,在每次合并的时候,有一个原则,因为只有两个监狱,多以我的敌人的敌人应该和我关在同一个监狱里,换句话说,敌人的敌人是朋友。在每次合并的时候,我应当与我敌人的敌人的集合合并,敌人应当与我的敌人的集合合并。
AC CODE:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define maxn 20005
using namespace std;
struct node
{
int x,y,data;
};
node a[maxn*5];
int f[maxn<<1],n,m;
inline bool cmp(node xx,node yy)
{
return xx.data>yy.data;
}
inline int find(int k)
{
if(f[k]==k) return f[k];
return f[k]=find(f[k]);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
f[i]=i; f[i+n]=i+n;
}
for(int i=1;i<=m;i++)
scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].data);
sort(a+1,a+m+1,cmp);
for(int i=1;i<=m;i++)
{
int x_friend=find(a[i].x),x_enemy=find(a[i].x+n);
int y_friend=find(a[i].y),y_enemy=find(a[i].y+n);
if(x_friend==y_friend)
{
printf("%d",a[i].data);
return 0;
}
f[x_friend]=y_enemy;
f[x_enemy]=y_friend;
if(i==m)
{
printf("0");
return 0;
}
}
return 0;
}