其他分享
首页 > 其他分享> > [每日一题]:[NOIP2010]关押罪犯 -- 并查集

[每日一题]:[NOIP2010]关押罪犯 -- 并查集

作者:互联网

题目:


题目链接:

https://vjudge.net/contest/369847#problem/A

考察点:

并查集的变种、并查集补集

并查集补集是个什么东东:

相信大家听到补集这个词语应该不会感到陌生,但是在这里并查集补集的概念有一丢丢不同。
这里通俗一点说就是一种相反的关系。比如 A 和 B 都有 10 元钱,现在 A 看上 B 了,决定
将自己的小金库交给 B 保管,那么现在 A 就剩 0 元,相应的 B 会增加 10 元。
这是一种相对的关系,放到并查集会对应什么呢?
拿这两个监狱来说,如果说 A 和 B 在同一个监狱,那么相反的肯定是 A 和 B 不在同一个监狱.
相对应的就是两个集合 -- 两种状态,of course 有多种状态的话肯定对应这多个集合。

你也许会有疑问,为什么这种补集的方法要开几倍的空间,开一倍空间是不够吗?
(菜鸡的我会有这种疑问,哈哈,大佬可以跳过了)
在这类并查集问题中,我们往往不仅要知道两者的显性的关系,还需要了解两者之间的隐式关系。
然而我们在并查集查找时为了便于快速查找,采用了压缩路径的方法,导致我们原先的一些关系
乱套了,比如 A、B、C 三个人搞三角恋,在同一个集合中了,你现在能清楚的知道谁 love 谁
吗?显然不够明了。

那么我们在合并时要怎样合并呢?
首先需要扩大一下数组,如果中有 x 种关系,就扩大 x 倍。
拿这道题来说:
我们需要开两倍: A: 1 2 3
               B: 1 2 3
A 集合中的 1 对应 B 集合中 1 ,其他同理。
如果说我们知道 1 和 3 在同一个监狱,而且他们的危险最大,我们就需要讲两者分开。
所以 A 中的 1 和 B 中的 3 合并
     A 中的 3 和 B 中的 1 合并
从而得出两者的相对关系。

侃侃:

相信你看过上面的解释后会有一种拨开云雾见日月的感觉,如果有,是我的荣幸,如果
没有让你足够清楚,可能是我能力有限,如果你有好的见解希望分享给我。
下面说说这道题:
首先,威胁最大的一定不能够在同一个监狱,如果可以的话就没我们的事了。
所以,我们将所有的关系之间产生的威胁 从大到小进行排序。
其次,就开始并查集了,如果两者可以出现在不同的集合,就将两个人分在不同的集合中,表示
将两个人分在了不同的监狱。
为了更好的解释这个关系,请看下图:

Code:

#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int maxn = 2e5 + 10;

typedef long long LL;

struct node {
	int u,v,w;
}person[maxn];

int fa[maxn << 1];

int n,m;

bool cmp(node a,node b) {
	return a.w > b.w;
} 

int get(int x) {
	return x == fa[x] ? x : fa[x] = get(fa[x]);
}

void Union(int x,int y) {
	int xx = get(x);
	int yy = get(y);
	if(xx == yy) return ;
	fa[yy] = xx;
	return ; 
}

int main(void) {
	scanf("%d%d",&n,&m);
	for(int i = 1; i <= m; i ++) {
		scanf("%d%d%d",&person[i].u,&person[i].v,&person[i].w);
	}
	// 初始化 
	for(int i = 1; i <= (n << 1); i ++) {
		fa[i] = i;
	}
	// 先分开打的火热的 
	sort(person + 1,person + 1 + m,cmp);
	// 看是不是所有人都可以入洞房 
	bool vis = false;
	
	for(int i = 1; i <= m; i ++) {
		int x = get(person[i].u);
		int y = get(person[i].v);
		// 说明还不是情敌 
		if(x != y) {
			Union(x,y + n);
			Union(y,x + n);
		} else {
		// 是情敌,必须干掉一个 
			printf("%d\n",person[i].w);
			vis = true;
			break;
		}
	}
	if(!vis)
	puts("0");
	return 0;
} 

后记:

通过这道题,也学到了不少东西。
(人只能一心一意爱一个人,哈哈)
对这种扩展域并查集有了更清楚的认知,当初看食物链那道题很懵,有了并查集
补集的这个概念,就很好理解了。
还有一种二分的解法,有空要学习一下。
前路漫漫,仍需努力。

参考链接:

并查集补集

关押罪犯

标签:并查,NOIP2010,关押,int,查集,fa,集合,include
来源: https://www.cnblogs.com/prjruckyone/p/12763153.html