【题解】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;
}
posted @ 2019-08-13 19:32  zhz小蒟蒻  阅读(80)  评论(0编辑  收藏  举报