[NOIP2010] P1525关押罪犯题解

点击跳转了解题意

这是一道进阶的并查集的题目,其做法有两种,第一种是带权并查集,新开一个数组维护每个节点和根节点的关系,但由于博主太菜,这个做法研究了一下午也没有搞懂,所以就采用了我认为更好理解一点的扩展域并查集,这道题中要求输出的最大冲突值最小,所以贪心的来做,我们先按冲突值从大到小排一个序,尽量不让冲突值大的事件发生,题目中说共有两个监狱,有m对罪犯之间有冲突,只要这两个罪犯关在了同一间监狱里,这个冲突就会发生,所以从冲突值最大开始,依次将两个罪犯往不同的两个监狱里放,如果到哪一步放不进去了,当前这个冲突值即为答案。

那么问题来了,我们怎么样应用扩展域并查集维护这样一对关系呢,我们将fa开到2*n,其中[1,n]维护的是与自己在一个监狱里的人的集合,[n+1,2n]维护的是我的敌人所在监狱的集合,在每次合并的时候,有一个原则,因为只有两个监狱,多以我的敌人的敌人应该和我关在同一个监狱里,换句话说,敌人的敌人是朋友。在每次合并的时候,我应当与我敌人的敌人的集合合并,敌人应当与我的敌人的集合合并。

AC CODE:

#include
#include
#include
#include
#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;
}

你可能感兴趣的:(数据结构,题解,并查集)