当前位置: 首页 > news >正文

数据结构-并查集专题(1)

一、前言

因为要开始准备年底的校赛和明年年初的ACM、蓝桥杯、天梯赛,于是开始按专题梳理一下对应的知识点,先从简单入门又值得记录的内容开始,并查集首当其冲。

二、我的模板

虽然说是借用了jiangly鸽鸽的板子,但是自己也小做修改了部分(命名啥的,大体内容并没有修改,jiangly鸽鸽yyds)

#include <bits/stdc++.h>struct DisjointSet {int _n;std::vector<int> _fa,_size;DisjointSet(){}DisjointSet(int n){init(n);}void init(int n) {_fa.resize(n);std::iota(_fa.begin(),_fa.end(),0);_size.assign(n,1);}int find(int x) {if(x!=_fa[x]) {_fa[x] = find(_fa[x]);}return _fa[x];}bool same(int x,int y) {return find(x)==find(y);}bool merge(int x,int y) {int fx = find(x);int fy = find(y);if(fx!=fy) {_size[fx]+=_size[fy];_fa[fy] = fx;return true;}return false;}
};

三、并查集介绍

并查集(disjoint set),英文直译过来是不相交的集合。我们中文取名成并查集,是因为这类集合主要具有两个操作:并和查,并即合并两个集合,查即查询集合的某些信息。

3.1 并

如果有学习过树的知识,那么理解并查集就比较轻松了。“并”操作类似于将森林(两棵树)转化为一棵树,我们只需要让其中一个并查集的根节点的父亲结点指向另外一个并查集的根节点即可。当然选择的时候,我们倾向于让深度小的树的根节点指向深度大的树的根节点。不过后续用上路径压缩后,谁指向谁基本没有什么太大的影响。实际写代码中,我们主要的操作就是对一个并查集的根节点进行修改

3.2 查

1.查询某个节点的根节点 2.查询两个节点是否处于同一个并查集中 3.查询一共有几个并查集 4.查询节点数量最多的并查集和它的节点数量 5.查询节点位于自身并查集的深度

3.3 初始化

在没有进行任何合并前,一个并查集的根节点应该为它自身(切记写代码的时候不要忘记并查集的初始化,当然如果用我的模板的话就不会忘记初始化,构造函数已经写好初始化了)

3.4 路径压缩

正常来说,并查集合并的时间复杂度为O(1),而查询的最坏时间复杂度为O(n),常见的最坏情况就是只有左子树或者只有右子树的一棵树的查询。而如果我们在查询时顺带将查询路径上的结点的父节点属性顺带修改为真正的根节点,那么综合下来,时间复杂度将会被均摊成O(logn),这是一个非常优秀的优化。

四、专题训练

以我学校OJ题目展开训练

4.1 题目总览

4.2 1520: 数据结构-并查集-家庭问题

思路

这道题是很典型的用并查集做的题目,先对并查集进行初始化,我们直接把有关系的两人进行合并,如果已经处于同一个并查集中则不做操作。后续题目需要我们查询的是并查集的数量以及并查集中最大的节点数量

参考代码

前面都是并查集模板,注意构造并查集的时候至少把节点数+1(因为用于实现的vetcor下标从0开始,大部分题目的下标是从1开始的),最后查询的时候利用set进行排序输出即可

#include <bits/stdc++.h>
using i64 = long long;struct DisjointSet {int _n;std::vector<int> _fa,_size;DisjointSet(){}DisjointSet(int n){init(n);}void init(int n) {_fa.resize(n);std::iota(_fa.begin(),_fa.end(),0);_size.assign(n,1);}int find(int x) {if(x!=_fa[x]) _fa[x] = find(_fa[x]);return _fa[x];}bool same(int x,int y) {return find(x)==find(y);}bool merge(int x,int y) {int fx = find(x);int fy = find(y);if(fx!=fy) {_size[fy]+=_size[fx];_fa[fx] = fy;return true;}return false;}
};
using pii = std::pair<int,int>;int main() {std::cin.tie(nullptr)->sync_with_stdio(false);int n,k;std::cin >> n >> k;DisjointSet disjointSet = DisjointSet(n+1);for(int i = 0;i<k;i++) {int mem1,mem2;std::cin >> mem1 >> mem2;disjointSet.merge(mem1,mem2);}std::set<pii> st;for(int i = 1;i<=n;i++) {int t = disjointSet.find(i);st.insert(std::make_pair(disjointSet._size[t],t));}std::cout << st.size() << ' ' << (*st.rbegin()).first << '\n';return 0;
}

4.3 1565: 并查集-团伙

思路

这个题由于enemy的存在,需要多维护一个ene[]数组,ene[]数组初始化为0,如果遇到p等于0的时候,分别判断x和y的ene是否为0,如果为0,则代表他们当前没有enemy,则把ene值设置成对方,即x和y分别属于两个并查集中,如果当前存在ene,那么就把自己合并到他们enemy的并查集中,因为敌人的敌人是朋友。至于p等于1的情况,当做正常并查集的合并来做。最后输出并查集的数量(计算父节点等于自身的节点数量)即可

参考代码

#include <bits/stdc++.h>
using i64 = long long;struct DisjointSet {int _n;std::vector<int> _fa,_size;DisjointSet(){}DisjointSet(int n){init(n);}void init(int n) {_fa.resize(n);std::iota(_fa.begin(),_fa.end(),0);_size.assign(n,1);}int find(int x) {if(x!=_fa[x]) {_fa[x] = find(_fa[x]);}return _fa[x];}bool same(int x,int y) {return find(x)==find(y);}bool merge(int x,int y) {int fx = find(x);int fy = find(y);if(fx!=fy) {_size[fy]+=_size[fx];_fa[fx] = fy;return true;}return false;}
};int main() {std::cin.tie(nullptr)->sync_with_stdio(false);int n,m;std::cin >> n >> m;DisjointSet disjointSet = DisjointSet(n+1);std::vector<int> ene(n+1,0);for(int i = 0;i<m;i++) {int t,x,y;std::cin >> t >> x >> y;if(t) {//enemyif(ene[x]) {disjointSet.merge(y,ene[x]);}else {ene[x] = y;}if(ene[y]) {disjointSet.merge(x,ene[y]);}else {ene[y] = x;}}else {//frienddisjointSet.merge(x,y);}}int ans = 0;for(int i = 1;i<=n;i++) {if(disjointSet.find(i)==i) ++ans;}std::cout << ans << '\n';return 0;
}

4.4 1566: 并查集-打击犯罪

思路

题目有点反直觉,我一开始想着从1到k依次进行判断,断开某个关系后可以使得他们中最大的一个危险程度小于等于n/2,然后后来发现写起来很困难。正难则反,因此我们考虑从n开始循环,跑到1结束,每次循环让这个循环变量i这个节点和它有关系并且大于k的j节点进行合并,然后看看是否有危险程度大于n/2的并查集即可。

参考代码

#include <bits/stdc++.h>
using i64 = long long;struct DisjointSet {int _n;std::vector<int> _fa,_size;DisjointSet(){}DisjointSet(int n){init(n);}void init(int n) {_fa.resize(n);std::iota(_fa.begin(),_fa.end(),0);_size.assign(n,1);}int find(int x) {if(x!=_fa[x]) {_fa[x] = find(_fa[x]);}return _fa[x];}bool same(int x,int y) {return find(x)==find(y);}bool merge(int x,int y) {int fx = find(x);int fy = find(y);if(fx!=fy) {_size[fx]+=_size[fy];_fa[fy] = fx;return true;}return false;}
};int main() {std::cin.tie(nullptr)->sync_with_stdio(false);int n;std::cin >> n;std::vector<std::vector<int>> edges(n+1,std::vector<int>());DisjointSet disjointSet = DisjointSet(n+1);for(int i = 0;i<n;i++) {int siz;std::cin >> siz;for(int j = 0;j<siz;j++) {int t;std::cin >> t;edges[i+1].emplace_back(t);}}for(int k = n;k>=1;k--) {for(auto &edge:edges[k]) {if(edge>k) {disjointSet.merge(k,edge);}}if(disjointSet._size[disjointSet.find(k)]>n/2) {std::cout << k << '\n';return 0;}}return 0;
}

4.5 1567: 并查集-搭配购买

思路

并查集+01背包,中间对数据的存储搞得我很头疼,用了一堆vector,先把c[]和d[]数组存起来,做了并查集的合并后,再算一个并查集c[]的总和sumc[]以及d[]的总和sumd[],然后把计算好的总和放进real_c[]和real_d[]数组中,再使用一个dp[]数组做一遍01背包,最后的dp[w]输出即可

参考代码

#include <bits/stdc++.h>
using i64 = long long;struct DisjointSet {int _n;std::vector<int> _fa,_size;DisjointSet(){}DisjointSet(int n){init(n);}void init(int n) {_fa.resize(n);std::iota(_fa.begin(),_fa.end(),0);_size.assign(n,1);}int find(int x) {if(x!=_fa[x]) {_fa[x] = find(_fa[x]);}return _fa[x];}bool same(int x,int y) {return find(x)==find(y);}bool merge(int x,int y) {int fx = find(x);int fy = find(y);if(fx!=fy) {_size[fx]+=_size[fy];_fa[fy] = fx;return true;}return false;}
};
using pii = std::pair<int,int>;int main() {std::cin.tie(nullptr)->sync_with_stdio(false);int n,m,w;std::cin >> n >> m >> w;std::vector<int> c,d;for(int i = 0;i<n;i++) {int tmpc,tmpd;std::cin >> tmpc >> tmpd;c.emplace_back(tmpc);d.emplace_back(tmpd);}DisjointSet disjointSet = DisjointSet(n+1);for(int i = 0;i<m;i++) {int x,y;std::cin >> x >> y;disjointSet.merge(x,y);}std::vector<int> sumc(n+1,0),sumd(n+1,0);for(int i = 1;i<=n;i++) {int t = disjointSet.find(i);sumc[t] += c[i-1];sumd[t] += d[i-1];}std::vector<int> real_c,real_d;for(int i = 1;i<=n;i++) {if(sumc[i]!=0) {real_c.emplace_back(sumc[i]);real_d.emplace_back(sumd[i]);}}std::vector<int> dp(w+1,0);for(int i = 0;i<real_c.size();i++) {for(int j = w;j>=real_c[i];j--) {dp[j] = std::max(dp[j],dp[j-real_c[i]]+real_d[i]);}}std::cout << dp[w] << '\n';return 0;
}

五、后记

剩下的题目及并查集的进一步运用(求最小生成树的Kruskal算法)见后续的(2)

相关文章:

数据结构-并查集专题(1)

一、前言 因为要开始准备年底的校赛和明年年初的ACM、蓝桥杯、天梯赛&#xff0c;于是开始按专题梳理一下对应的知识点&#xff0c;先从简单入门又值得记录的内容开始&#xff0c;并查集首当其冲。 二、我的模板 虽然说是借用了jiangly鸽鸽的板子&#xff0c;但是自己也小做…...

共享汽车管理新纪元:SpringBoot框架应用

4系统概要设计 4.1概述 本系统采用B/S结构(Browser/Server,浏览器/服务器结构)和基于Web服务两种模式&#xff0c;是一个适用于Internet环境下的模型结构。只要用户能连上Internet,便可以在任何时间、任何地点使用。系统工作原理图如图4-1所示&#xff1a; 图4-1系统工作原理…...

道可云人工智能元宇宙每日资讯|《中国生成式人工智能应用与实践展望》白皮书发布

道可云元宇宙每日简报&#xff08;2024年11月6日&#xff09;讯&#xff0c;今日元宇宙新鲜事有&#xff1a; 《重庆市“机器人”应用行动计划&#xff08;2024—2027年&#xff09;》发布 近日&#xff0c;重庆市经济和信息化委员会、重庆市教育委员会等八部门印发《重庆市“…...

kaggle学习 eloData项目(1)-数据校验

文章目录 kaggle学习 eloData项目&#xff08;1&#xff09;-数据校验&#xff08;1&#xff09; 数据基本情况查看&#xff08;2&#xff09; 数据校验&#xff08;3&#xff09; 数据探究 小结 kaggle学习 eloData项目&#xff08;1&#xff09;-数据校验 不能懈怠&#xff0…...

ORACLE RAC用DNS服务器的配置

一、搭建本地YUM源 二、安装DNS全部组建 yum -y install bind* 三、规划您RAC集群所有IP #public 192.168.16.111 rac1.ntt.com rac1 192.168.16.112 rac2.ntt.com rac2 192.168.16.121 rac3.ntt.com rac3 192.168.16.122 rac4.ntt.com rac4 #private 10.10.10.111 rac1-pr…...

vue3 + vite 实现版本更新检查(检测到版本更新时提醒用户刷新页面)

背景 当一个页面很久没刷新&#xff0c;又突然点到页面。由于一些文件是因为动态加载的&#xff0c;当重编后&#xff08;如前后端发版后&#xff09;&#xff0c;这些文件会发生变化&#xff0c;就会出现加载不到的情况。进而导致正在使用的用户&#xff0c;点击页面发现加载…...

【CSP】爆零的独特姿势

硝烟散&#xff0c;繁花尽&#xff0c;第一次CSP折戟沉沙。 代码拿回来&#xff0c;花几分钟订正下&#xff0c;就是300分。 然而&#xff0c;实战只有100分&#xff0c;还是偷懒得的幸运&#xff0c;觉得第一题题目太简单懒得用文件IO调试... ... 啥也不说了&#xff0c;上图。…...

Git仓库

Git初始 概念 一个免费开源&#xff0c;分布式的代码版本控制系统&#xff0c;帮助开发团队维护代码 作用 记录代码内容&#xff0c;&#xff0c;切换代码版本&#xff0c;多人开发时高效合并代码内容 如何学&#xff1a; 个人本机使用&#xff1a;Git基础命令和概念 多…...

【科研日常】论文投稿的几大状态

Manuscript Submitted&#xff08;Submitted to Journal&#xff09;&#xff1a;表示论文已经投稿成功&#xff0c;等待期刊工作人员检查论文格式排版、重复率是否符合要求&#xff0c;符合要求的文章会分配给期刊编辑进行处理。 Awaiting Admin Processing&#xff1a;意为等…...

SSLHandshakeException错误解决方案

1、错误提示 调用Http工具报如下异常信息&#xff1a; cn.hutool.core.io.IORuntimeException: SSLHandshakeException: Received fatal alert: handshake_failure2、查询问题 一开始我以为是代码bug&#xff0c;网络bug甚至是配置环境未生效&#xff0c;找了一大圈&#xf…...

python数据结构基础(7)

本节学习最后一种数据结构---图,在很多问题中应用图可以帮助构建思维空间,快速理清思路,解决复杂问题. 图就是一些顶点的集合,这些顶点通过一系列边链接起来.根据边的有向和无向,图分为有向图和无向图.有时图的边上带有权重,本节暂时不将权重作为重点. 计算机通过邻接表或者邻…...

【系统集成项目管理工程师】英语词汇对照表-项目管理类

英语单词&#xff08;项目管理类&#xff09;中文解释Activity活动Accept验收Acceptable Quality Level可接受的质量水平Acceptance Standard验收标准Acquisition Plan Review采购计划评审Action处理Active On the Arrow双代号网络图Activity Based Costing (ABC)基于活动的成本…...

购物车-多元素组合动画css

学习 渡一课程 多元素组合动画 练习。 在我们开发购物车功能时&#xff0c;经常会有点击添加按钮&#xff0c;就会有一个小圆点掉进购物车的动画&#xff0c;如下图所示&#xff0c;今天我们通过css来实现。 首先实现多元素组合动画 直接上代码&#xff0c;可以复制到本地使用…...

【计网不挂科】计算机网络期末考试——【选择题&填空题&判断题&简述题】题库(3)

前言 大家好吖&#xff0c;欢迎来到 YY 滴计算机网络 系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过C的老铁 本博客主要内容&#xff0c;收纳了一部门基本的计算机网络题目&#xff0c;供yy应对期中考试复习。大家可以参考 欢迎订阅 YY滴其他专栏&#xff01;…...

[ vulnhub靶机通关篇 ] 渗透测试综合靶场 DarkHole:1 通关详解 (附靶机搭建教程)

&#x1f36c; 博主介绍 &#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 _PowerShell &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【数据通信】 【通讯安全】 【web安全】【面试分析】 &#x1f389;点赞➕评论➕收藏 养成习…...

【LeetCode】移除链表中等于设定值的元素、反转链表

主页&#xff1a;HABUO&#x1f341;主页&#xff1a;HABUO &#x1f31c;有时候世界虽然是假的&#xff0c;但并不缺少真心对待我们的人&#x1f31b; 1. 移除链表中设定值的元素 题目&#xff1a;给你一个链表的头节点 head 和一个整数 val &#xff0c;请你删除链表中所…...

Redis - 主从复制

在分布式系统中为了解决单点问题&#xff0c;通常会把数据复制多个副本部署到其他服务器&#xff0c;满⾜故障恢 复和负载均衡等需求。Redis也是如此&#xff0c;它为我们提供了复制的功能&#xff0c;实现了相同数据的多个Redis副 本。复制功能是⾼可⽤Redis的基础&#xff0c…...

UE5 HLSL 学习笔记

half的取值范围是整形的-60000 到 60000&#xff0c;考虑带宽的情况下使用half vector默认为float4 访问可以.xyzw&#xff0c;也可以.rgba&#xff0c;也可以[index]&#xff0c;且顺序可以变&#xff0c;比如说.yzwx 矩阵的获取值的方式 第一个行代表获取第1行第0号元素 第…...

一个简单ASP.NET购物车设计

思路&#xff1a; 创建一个多选列表 在cs文件里初始化购物车会话变量,同&#xff0c;创建一个新的 List<string> 并将其赋值给会话状态中的 "Cart" 键–&#xff08;利用Session&#xff09; Session 是一种用于存储用户特定信息的对象&#xff0c;这些信息可…...

双向循环列表

双向循环列表的实现。 根据定义实现。不解释&#xff0c;具体细节看代码。 list.h #pragma once#pragma pack(1)typedef struct _MyListEntry {_MyListEntry* next;_MyListEntry* prev; }MyListEntry;#pragma pack()class MyListClass { public:MyListEntry* m_list0;int m_k…...

大型活动交通拥堵治理的视觉算法应用

大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动&#xff08;如演唱会、马拉松赛事、高考中考等&#xff09;期间&#xff0c;城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例&#xff0c;暖城商圈曾因观众集中离场导致周边…...

Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)

参考官方文档&#xff1a;https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java&#xff08;供 Kotlin 使用&#xff09; 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…...

Python 包管理器 uv 介绍

Python 包管理器 uv 全面介绍 uv 是由 Astral&#xff08;热门工具 Ruff 的开发者&#xff09;推出的下一代高性能 Python 包管理器和构建工具&#xff0c;用 Rust 编写。它旨在解决传统工具&#xff08;如 pip、virtualenv、pip-tools&#xff09;的性能瓶颈&#xff0c;同时…...

搭建DNS域名解析服务器(正向解析资源文件)

正向解析资源文件 1&#xff09;准备工作 服务端及客户端都关闭安全软件 [rootlocalhost ~]# systemctl stop firewalld [rootlocalhost ~]# setenforce 0 2&#xff09;服务端安装软件&#xff1a;bind 1.配置yum源 [rootlocalhost ~]# cat /etc/yum.repos.d/base.repo [Base…...

Rust 开发环境搭建

环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行&#xff1a; rustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu ​ 2、Hello World fn main() { println…...

日常一水C

多态 言简意赅&#xff1a;就是一个对象面对同一事件时做出的不同反应 而之前的继承中说过&#xff0c;当子类和父类的函数名相同时&#xff0c;会隐藏父类的同名函数转而调用子类的同名函数&#xff0c;如果要调用父类的同名函数&#xff0c;那么就需要对父类进行引用&#…...

通过 Ansible 在 Windows 2022 上安装 IIS Web 服务器

拓扑结构 这是一个用于通过 Ansible 部署 IIS Web 服务器的实验室拓扑。 前提条件&#xff1a; 在被管理的节点上安装WinRm 准备一张自签名的证书 开放防火墙入站tcp 5985 5986端口 准备自签名证书 PS C:\Users\azureuser> $cert New-SelfSignedCertificate -DnsName &…...

数据结构:递归的种类(Types of Recursion)

目录 尾递归&#xff08;Tail Recursion&#xff09; 什么是 Loop&#xff08;循环&#xff09;&#xff1f; 复杂度分析 头递归&#xff08;Head Recursion&#xff09; 树形递归&#xff08;Tree Recursion&#xff09; 线性递归&#xff08;Linear Recursion&#xff09;…...

数据分析六部曲?

引言 上一章我们说到了数据分析六部曲&#xff0c;何谓六部曲呢&#xff1f; 其实啊&#xff0c;数据分析没那么难&#xff0c;只要掌握了下面这六个步骤&#xff0c;也就是数据分析六部曲&#xff0c;就算你是个啥都不懂的小白&#xff0c;也能慢慢上手做数据分析啦。 第一…...

大数据驱动企业决策智能化的路径与实践

&#x1f4dd;个人主页&#x1f339;&#xff1a;慌ZHANG-CSDN博客 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; 一、引言&#xff1a;数据驱动的企业竞争力重构 在这个瞬息万变的商业时代&#xff0c;“快者胜”的竞争逻辑愈发明显。企业如何在复杂环…...