zoukankan      html  css  js  c++  java
  • [每日一题]:[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
    ",person[i].w);
    			vis = true;
    			break;
    		}
    	}
    	if(!vis)
    	puts("0");
    	return 0;
    } 
    

    后记:

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

    参考链接:

    并查集补集

    关押罪犯

  • 相关阅读:
    最大公约数与最小公倍数
    素数筛
    基础数学问题
    考试前打模板
    斐波那契公约数
    期望及期望dp
    状压dp总结
    树链剖分学习
    B君的教育
    [noip2016]愤怒的小鸟
  • 原文地址:https://www.cnblogs.com/prjruckyone/p/12763153.html
Copyright © 2011-2022 走看看