【题解】P1525 关押罪犯
这道题是一道比较好的并查集的题目,蒟蒻顿时感觉我学了一个假的并查集。
思路
首先,这道题的意思是:
给你 \(n\) 个点,将他们任意分成两边,求这些点之前权值最大的边尽量的小,求这个值。
我们如何用并查集来做呢?
首先,我们将所有边从大到小排序,对于每两个点 \(x\) 和 \(y\) ,我们将 \(x\) 和 \(y\) 的敌人放一起, \(x\)的敌人和 \(y\) 放一起,敌人的敌人就是我的朋友。
当两个点不得不连接在一起时,那么我们就输出这条边的边权,特别的,如果没有两个点被迫连在一起,那么就需要输出 \(0\)
为什么这样做是对的呢?
首先,我们的边权是从大到小排的,当第一次出现两个不得不连接在一起的点时,当前这条边的边权肯定是最大的。
那么,对于第 \(i\) 个点的敌人 \(b[i]\) ,它与 \(i\) 的连边肯定是最大的,那么对于 \(x\) 和 \(y\) ,\(x\) 连上 \(y\) 的敌人肯定要比 \(x\) 和 \(y\) 连边的权值要小,不然的话,我们就会先遍历到这条边,再遍历 \(x\) 和 \(y\) 的这条边。
\(Code:\)
#include <iostream>
#include <cstdio>
#include <algorithm>
#define N 100011
using namespace std;
struct Node
{
int u,v,w;
}a[N];
int f[N],b[N];
bool cmp(Node x,Node y)
{
return x.w>y.w;
}
int getf(int v)
{
return f[v]==v?v:f[v]=getf(f[v]);
}
void merge(int x,int y)
{
int t1=getf(x),t2=getf(y);
if(t1!=t2) f[t2]=t1;
return;
}
bool check(int x,int y)
{
int t1=getf(x),t2=getf(y);
if(t1==t2) return true;
else return false;
}
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
int n,m;
scanf("%d %d",&n,&m);
for(int i=1;i<=m;++i)
scanf("%d %d %d",&a[i].u,&a[i].v,&a[i].w);
sort(a+1,a+m+1,cmp);
for(int i=1;i<=n;++i) f[i]=i;
for(int i=1;i<=m+1;++i)
{
int x,y;
x=a[i].u; y=a[i].v;
int t1=getf(x),t2=getf(y);
if(check(x,y))
{
printf("%d",a[i].w);
return 0;
}
if(!b[x])
b[x]=y;
else
merge(b[x],y);
if(!b[y])
b[y]=x;
else
merge(b[y],x);
}
return 0;
}