做题记录:P1525 关押罪犯(洛谷)

时间:2021-09-06 14:35:40

P1525 关押罪犯

/*在这一道题中并查集的作用是:将同一个*里的罪犯合并到一起。
思路:将每对罪犯之间的怨气值从大到小排序,再依次把他们分到不
同的两个*里,当发现这一对罪犯已经在同一个*里时,就说明
他们已经不能再分开了(分开了就不是最优了)。此时,这一对罪犯
之间的怨气值就是答案。值得注意的是,当没有冲突发生时,要记得
输出0。那么,我们应该如何用并查集实现以上的思路呢?首先说几个
关键词语的含义:
1、“敌人”:如果一对罪犯被分到两个不同的*里,那么他们就互为“敌人”。
2、“朋友”:如果一对罪犯被分到一个*里,那么他们就互为“朋友”。
需要明确的一点是“敌人”的“敌人”,就是“朋友”。
当分开一对罪犯时(假设他们的名字叫x和y),若x还没有“敌人”,那么y就
是他的“敌人”(因为他们被分开了嘛);否则就把y所在的集合与x的“敌人”
所在的集合合并,因为x是y的“敌人”,所以x的“敌人”就是y的“敌人”的
“敌人”(即朋友),将y所在的集合与x的“敌人”所在的集合合并是因为x的
“敌人”和x关在不同的*里,而y又和x关在不同的*里,并且只有两个监
狱,所以x的“敌人”所在的集合里面的人和y所在的集合里面的人一定关在一
个*里。对y的“敌人”也这样处理一遍就可以了。
*/
#include
<iostream>
#include
<cstdio>
#include
<algorithm>
using namespace std;
struct edge//存罪犯之间的怨气关系
{
int u;
int v;
int w;
}e[
100005];
bool cmp(edge x,edge y)//将怨气值从大到小排序的排序函数
{
return x.w>y.w;
}
//f为用作实现并查集的数组,表示的是第i个罪犯的“祖先”是谁,enemy储存的是第i个罪犯的某个敌人
int f[20005],enemy[20005];
int find_(int x)//并查集查找函数
{
if(f[x]==x) return x;
return f[x]=find_(f[x]);
}
void merge_(int x,int y)//并查集合并函数
{
int t1=find_(x),t2=find_(y);
if(t1!=t2) f[t2]=t1;
return;
}
int main()
{
int n=0,m=0;
scanf(
"%d%d",&n,&m);
for(int i=1;i<=n;i++) f[i]=i;//并查集初始化
for(int i=1;i<=m;i++) scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
sort(e
+1,e+m+1,cmp);//将怨气值从大到小排序
for(int i=1;i<=m;i++)
{
if(find_(e[i].u)==find_(e[i].v))//如果这一对罪犯已经在同一个*里
{
printf(
"%d",e[i].w);//直接输出他们之间的怨气值
return 0;//结束程序
}
//如果这一对罪犯仍能分开
if(!enemy[e[i].u]) enemy[e[i].u]=e[i].v;//如果u还没有敌人,那么v就是他的敌人
else merge_(e[i].v,enemy[e[i].u]);//否则,就将v与u的敌人合并
if(!enemy[e[i].v]) enemy[e[i].v]=e[i].u;//如果v还没有敌人,那么u就是他的敌人
else merge_(e[i].u,enemy[e[i].v]);//否则,就将u与v的敌人合并
}
printf(
"%d",0);//记得输出0
return 0;
}