算法------(11)并查集

例题:

(1)Acwing 836.合并集合

        并查集就是把每一个集合看成一棵树,记录每个节点的父节点。合并集合就是把一棵树变成另一棵树的子树,即把一棵树的父节点变为另一棵树的父节点的儿子。查询是否在同一集合就是看他们的根节点是否相同。在查找过程中可以路径加速,把每个点直接连到根节点。

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e5+10;
int p[N];
int find(int x){if(p[x]!=x) p[x] = find(p[x]);return p[x];
}
int main()
{int n,m;scanf("%d%d", &n, &m);for(int i = 1;i<=n;i++){p[i] = i;}for(int i = 0;i<m;i++){char op[2];int x,y;scanf("%s%d%d",op,&x,&y);if(op[0] == 'M'){p[find(x)] = find(y);}else{if(find(x)==find(y)) printf("Yes\n");else printf("No\n");}}return 0;
}

(2) Acwing 837.连通块中点的数量

         并查集板子题,把每个联通块当成一棵树,连边操作就等于将联通块合并,查询是否在同一个联通块中就等于查询其根节点是否相同,询问数量需要额外维护一个size数组,每次合并时根节点的size要加上被合并的根节点的size。如果合并时两个节点的根节点相同则无需合并,否则size会加倍。

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e5+10;
int sz[N],p[N];
int find(int x){if(p[x]!=x) p[x] = find(p[x]);return p[x];
}
int main()
{int n,m;scanf("%d%d", &n, &m);for(int i = 1;i<=n;i++){p[i] = i;sz[i] = 1;}for(int i = 0;i<m;i++){char op[2];int x,y;scanf("%s",op);if(op[0] == 'C'){scanf("%d%d", &x, &y);if(find(x)!=find(y)){sz[find(x)] += sz[find(y)];p[find(y)] = find(x);}}else if(op[1] == '1'){scanf("%d%d", &x, &y);if(find(x) == find(y)) printf("Yes\n");else printf("No\n");}else{scanf("%d", &x);printf("%d\n",sz[find(x)]);}}return 0;
}

(3)AcWing 240. 食物链

        由于是一个环形食物链,因此可以使用带权并查集,记录每个节点到根节点的距离,通过%3所得的数判断两者间的关系。具体代码有注释。 

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e5+10;
int d[N],p[N];
int find(int x){if(p[x]!=x){int t = find(p[x]);//把该点的父节点以上的所有节点全部连到根节点d[x] += d[p[x]];//此时d[p[x]]就是p[x]到根节点的距离p[x] = t;//p[x]连到根节点}return p[x];
}
int main()
{int n,m,res = 0;scanf("%d%d", &n, &m);for(int i = 1;i<=n;i++){p[i] = i;}while (m -- ){int s,x,y;scanf("%d%d%d",&s, &x, &y);if(x>n||y>n){res++;continue;}else if(s == 1){int px = find(x),py = find(y);if(px == py){if((d[x]-d[y])%3) res++;//同类则模0continue;}else{p[px] = py;d[px] = d[y] - d[x];}}else{int px = find(x),py = find(y);if(px == py){if((d[x]-d[y]-1)%3) res++;//可能存在一正一负因此不能==1continue;}else{p[px] = py;d[px] = d[y] - d[x] + 1;}}}printf("%d",res);return 0;
}

练习:(1)Leetcode 128.最长相关序列

         把数组里的每一个数看做一个节点,而每个节点可以与权值比自己本身权值小1的节点相连。对于数组中的每个数,如果权值比他小1的数在哈希表中存在并且两者不具有相同的根结点时,由于数组中不存在重复元素,且每次只会把大的数接在小的数后面,因此两者集合必然是连续的。所以先进行去重然后开始建树,由于存在负数因此利用哈希表储存每个数的下标防止访问越界。问题可以转化为最大的联通块中点的数量。

class Solution {
public:unordered_map<int,int> x;int p[100010],sz[100010];int find(int x){if(p[x]!=x) p[x] = find(p[x]);return p[x];}int longestConsecutive(vector<int>& nums) {sort(nums.begin(),nums.end());nums.erase(unique(nums.begin(),nums.end()),nums.end());int n = nums.size();for(int i = 1;i<=n;i++){x[nums[i-1]] = i;p[i] = i;sz[i] = 1;}for(int i = 0;i<n;i++){if(x[nums[i]-1]&&find(x[nums[i]-1]) != find(x[nums[i]]) ){sz[find(x[nums[i]-1])] += sz[find(x[nums[i]])];p[find(x[nums[i]])] = find(x[nums[i]-1]);}}int ans = 0;for(int i = 0;i<n;i++){if(p[x[nums[i]]] == x[nums[i]]) ans = max(ans,sz[x[nums[i]]]);}return ans;}
};

(2)Leetcode 684.冗余连接

        没做出来。。

        遍历每条边,如果该边的两个端点属于一个集合内,那么就说明这条边导致了一个环的构成。返回这条边。否则把两个端点加到同一个集合内。由于题目规定只会多出一条边(只会存在一个环),因此出现的这条边一定是构成环的最后一条边,因为假如后面还存在能构成环的边则不满足只有一个环的条件,因此这条边就是构成环的最后一条边。

class Solution {
public:int p[1010];int find(int x){if(p[x]!=x) p[x] = find(p[x]);return p[x];}vector<int> findRedundantConnection(vector<vector<int>>& edges) {int n = edges.size();for(int i = 1;i<=n;i++) p[i] = i;for(auto c:edges){int a = find(c[0]),b = find(c[1]);if(a==b) return{c[0],c[1]};else{p[a] = b;}}return {0,0};}
};

(3)Leetcode 1202.交换字符串中的元素

        并查集应该不是最佳解。。不过没想出来啥别的方法。。

        第一步:把所有能交换的位置构成一个集合。第二步:遍历字符串,把每个字母加入其根节点映射的优先队列(可以自动按字典序排序)第三步:遍历字符串,用一个空字符串记录结果。在每个字母对应的位置放上其根节点对应的优先队列中第一个字母(按字典序最小的字母),然后该元素出队。

class Solution {
public:int p[100010];int find(int x){if(p[x]!=x) p[x] = find(p[x]);return p[x];}string smallestStringWithSwaps(string s, vector<vector<int>>& pairs) {unordered_map<int,priority_queue<int,vector<int>,greater<int>>> x;int n = s.length();for(int i = 0;i<n;i++) p[i] = i;for(auto c:pairs){int a = find(c[0]),b = find(c[1]);if(a!=b) p[a] = b;}   for(int i = 0;i<n;i++) x[find(i)].push((int)s[i]);string res = "";for(int i = 0;i<n;i++){char ch = (char)x[find(i)].top();x[find(i)].pop();res+=ch;}return res;}
};

(4)Leetcode 886.可能的二分法

        判断二分图的最好方法并不是并查集,但是。。用并查集没做出来。。

        把每一个人的关系不好的人放入一个集合中,如果这个集合的人里面有人跟该人的根节点相同说明其属于同一个集合,不成立,反之则把他们加入同一个集合。

class Solution {
public:unordered_map<int,vector<int>> x;int p[2010];int find(int x){if(p[x]!=x) p[x] = find(p[x]);return p[x];}bool possibleBipartition(int n, vector<vector<int>>& dislikes) {if(!dislikes.size()) return true;for(auto c:dislikes){x[c[0]].push_back(c[1]);x[c[1]].push_back(c[0]);}for(int i = 1;i<=n;i++) p[i] = i;for(int i = 1;i<=n;i++){if(x[i].empty()) continue;int a = find(i),b = find(x[i][0]);for(auto c:x[i]){if(find(c) == a) return false;p[find(c)] = b;}}return true;}
};

(5) P8686 [蓝桥杯 2019 省 A] 修改数组

         。。。其实还是没太理解。。

        首先把每一个数的根节点设为其本身,然后对于每次输入的数,如果其根节点为本身,说明其第一次出现,因此把根节点更新为原先的根节点+1,否则查找其根节点(由于每次都加1,因此 从 其本身 到 其根节点-1 都已经出现过),然后输出根节点并且把 根节点+1 作为根节点的新父节点(这个父节点有可能还会有父节点,但并不影响,可以理解为两条链相连成为一条更长的新链)

        初始化时要把后面的节点一起初始化,否则更新时由于后续某些节点的根节点变为0就会出错。。

#include <iostream>
using namespace std;
const int N = 100010;
typedef long long ll;
ll p[N];
ll find(ll x){if(p[x]!=x) p[x] = find(p[x]);return p[x];
} 
int main(int argc, char** argv) {ll n;scanf("%d",&n);for(int i = 1;i<=100010;i++) p[i] = i;for(int i = 1;i<=n;i++){ll x;scanf("%lld",&x);printf("%lld ",find(x));p[find(x)] = find(x) + 1;}return 0;
}

(7)洛谷 P1111 修复公路

        。。很烦。。        

        联通块的个数为1时就能完全通车。首先按照时间排序(分清路的条数和村庄的个数!!),然后遍历每一条路,假如两个路边上的村庄共用一个根节点则无视,否则联通块数-1,把两个村庄联通。每次判断所有联通块是否只剩一个,如果是则输出当前时间戳,如果最后还剩不止一个联通块则输出-1。这里判断联通块个数不需要每一次都遍历,只需要维护联通块个数即可(因为一开始每一个村庄就是一个联通块)

#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1010,M = 100010;
struct Node{int x,y,t; bool operator< (Node &a){return t < a.t;}
}road[M];
int p[N];
int find(int x){if(p[x]!=x) p[x] = find(p[x]);return p[x];
}
int main(int argc, char** argv) {int n,m;scanf("%d%d",&n,&m);int res = n; for(int i = 1;i<=n;i++) p[i] = i;for(int i = 0;i<m;i++){scanf("%d%d%d",&road[i].x,&road[i].y,&road[i].t);}sort(road,road+m);bool flag = 0;for(int i = 0;i<m;i++){int a = find(road[i].x),b = find(road[i].y),c = road[i].t;if(a == b) continue;else{res--;if(res==1){printf("%d",c);flag = 1;break;}p[a] = b;}}if(!flag) printf("-1");return 0;
}

(8)洛谷 P1455 搭配购买

        一个缝合题,01背包加并查集。。01背包写的很差,还是不太会用滚动数组。。后面再补一下吧。。

        用并查集把多个商品合成为一个商品,根节点代表商品本身。01背包不用滚动数组会MLE。更新时注意是更新根节点的价格和价值(因为是连接根节点)。

#include <iostream>
using namespace std;
const int N = 1e4+10;
int w[N],v[N],p[N],f[N];
struct Node{int w,v;
}shop[N];
int find(int x){if(p[x]!=x) p[x] = find(p[x]);return p[x];
}
int main(int argc, char** argv) {int n,m,w1;scanf("%d%d%d",&n,&m,&w1);for(int i = 1;i<=n;i++){scanf("%d%d",&w[i],&v[i]);p[i] = i;}for(int i = 0;i<m;i++){int a,b;scanf("%d%d",&a,&b);a = find(a),b = find(b);if(a==b) continue;p[a] = b;w[b] += w[a];v[b] += v[a];}int cnt = 1;for(int i = 1;i<=n;i++){if(p[i]!=i) continue;shop[cnt].w = w[i];shop[cnt].v = v[i];cnt++;}for(int i = 1;i<=n;i++){for(int j = w1;j>=shop[i].w;j--){f[j] = max(f[j],f[j-shop[i].w] + shop[i].v);}}printf("%d",f[w1]);return 0;
}

(9)P3420 SKA-Piggy Banks

        虽然做出来了。。但是感觉还是搞不太明白。。

        这道题之所以可以用并查集的原因并不是因为罐子之间存在钥匙联系,而是一个罐子只有一个钥匙且在另一个罐子里,但一个罐子可以装多把钥匙。因此我们每次把钥匙对应的罐子连在存放钥匙的罐子后面,则打开根节点的罐子就可以打开所有罐子。因此一个集合内的罐子就一定只需要打开一个罐子,题目也就改为求联通块的数目。因此这道题用并查集可以做的原因其实是每个钥匙唯一且对应了一个罐子。假如题目改为第i个数代表第i个罐子存放了第ai个罐子的钥匙,这道题就不能用并查集做了。。https://www.luogu.com.cn/discuss/684322icon-default.png?t=N7T8https://www.luogu.com.cn/discuss/684322        这是比较专业的解答。。。我只能用自己的角度去解释。。解释的不太好。。

(10)P1196 [NOI2002] 银河英雄传说

        。。确实是挺难的。。

        为了查询两个战舰之间的战舰数目,我们维护每一个战舰到根节点的距离,这样两个战舰之间的战舰数目就是到根节点的距离差-1。而由于我们维护到根节点的距离还需要知道当前集合内的战舰数量,因此还需要维护一个数组用来记录每个集合(根节点下)的战舰数。当然,由于每个节点都没法在合并时就被更新,因此我们可以在find函数时对每个节点进行更新。 

#include <iostream>
using namespace std;
const int N = 30010;
int p[N],dis[N],num[N];
int find(int x){if(p[x]!=x){int k = p[x]; p[x] = find(p[x]);dis[x] += dis[k];num[x] = num[p[x]];}return p[x];
}
int main(int argc, char** argv) {int t;scanf("%d",&t);for(int i = 1;i<=30000;i++){p[i] = i;num[i] = 1;}while(t--){char op[2];scanf("%s",op);int x,y;scanf("%d%d",&x,&y);if(op[0] == 'M'){x = find(x);y = find(y);if(x!=y){p[x] = y;dis[x] = num[y];num[y] += num[x];num[x] = num[y];}}else{if(find(x) == find(y)) printf("%d\n",abs(dis[x] - dis[y])-1);else printf("-1\n");}}return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/2776292.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

【 buuctf snake 】

需要用到 Serpent 加密&#xff0c;蛇也不一定是 snake&#xff0c;serpent 也是蛇的意思。 binwalk -e /Users/xxx/Downloads/snake/snake.jpgbinwalk 提取 key 中有 base64 编码&#xff0c;解密 图源自BUUCTF:snake_buuctf snake-CSDN博客 结果是 anaconda&#xff0c;还有…

GC调优工具

1、jstat 2、VisualVM GC tool插件 插件下载地址&#xff1a;https://blog.csdn.net/jushisi/article/details/109655175 3、Prometheus和Grafana监控

今日早报 每日精选15条新闻简报 每天一分钟 知晓天下事 2月9日,星期五

每天一分钟&#xff0c;知晓天下事&#xff01; 2024年2月9日 星期五 农历腊月三十 除夕 1、 三部门&#xff1a;各地不得挤占、挪用、截留、滞留优抚对象补助经费。 2、 校外培训《条例》出炉&#xff1a;明确在职教师、教研人员不得从事校外培训活动。 3、 2024年“全面降…

(十六)springboot实战——spring securtity的认证流程源码解析

前言 本节内容是关于spring security安全框架认证流程的源码分析&#xff0c;spring security的认证流程主要是在UsernamePasswordAuthenticationFilter过滤器中实现的。我们会通过源码层级的分析&#xff0c;了解清楚spring security的底层是如何实现用户的认证的。 正文 1…

放飞梦想,扬帆起航——1888粉丝福利总结

目录 1.祝福 2.准备 3.抽奖 4.制作 5.添加 6.成果 7.感谢 8.福利 9.祝福 1.祝福 马上就是除夕了&#xff0c;在这里提前预祝大家春节快乐&#xff0c;小芒果在这里给大家拜年了&#xff01; 2.准备 其实很早之前我就在幻想着哪一天我的粉丝量能突破1888&#xff0c;…

University Program VWF仿真步骤__全加器

本教程将以全加器为例&#xff0c;选择DE2-115开发板的Cyclone IV EP4CE115F29C7 FPGA&#xff0c;使用Quartus Lite v18.1&#xff0c;循序渐进的介绍如何创建Quartus工程&#xff0c;并使用Quartus Prime软件的University Program VWF工具创建波形文件&#xff0c;对全加器的…

MPLS VPN功能组件(4)

数据转发过程 VPN数据的转发 顶层公网标签 由LDP分配&#xff0c;指示LSR如何将标签报文从始发的源PE通过LSP标签交换到达目的PE 内层私网标签(VPN标签) 由MP-BGP分配&#xff0c;在将每一条客户路由变为VPNv4路由前缀时会自动为每一条VPNv4前缀关联一个标签 内层私网标签用于…

Ubuntu22.04 gnome-builder gnome C 应用程序习练笔记(三)

八、ui窗体创建要点 .h文件定义(popwindowf.h)&#xff0c; TEST_TYPE_WINDOW宏是要创建的窗口样式。 #pragma once #include <gtk/gtk.h> G_BEGIN_DECLS #define TEST_TYPE_WINDOW (test_window_get_type()) G_DECLARE_FINAL_TYPE (TestWindow, test_window, TEST, WI…

ChatGPT高效提问—prompt常见用法(续篇六)

ChatGPT高效提问—prompt常见用法&#xff08;续篇六&#xff09; 1.1 控制输出 ​ 控制输出是一种先进的自然语言处理技术&#xff0c;其能够在AI模型生成文本的过程中实现更高级别的控制。通过提供特定的输入&#xff0c;如模板、特定词语或约束性条件&#xff0c;从而精准…

rem基础+媒体查询+Less基础

一&#xff0c;rem基础 二&#xff0c;媒体查询 2.1什么是媒体查询 2.2语法规范 2.3媒体查询rem实现元素动态大小的变化 2.4 引入资源&#xff08;理解&#xff09; 三&#xff0c;Less基础 1 维护css的弊端 2 Less介绍 3 Less变量 变量命名规范 4 Less嵌套 5 Less…

LabVIEW热电偶自动校准系统

设计并实现一套基于LabVIEW平台的工业热电偶自动校准系统&#xff0c;通过自动化技术提高校准效率和精度&#xff0c;降低人力成本&#xff0c;确保温度测量的准确性和可靠性。 工业生产过程中&#xff0c;温度的准确测量对产品质量控制至关重要。传统的热电偶校准方式依赖人工…

【C++11】右值引用 | 移动构造赋值 | 万能引用 | 完美转发

文章目录 一、引言二、左值和右值什么是左值什么是右值 三、左值引用和右值引用左值引用右值引用左值引用与右值引用的比较 四、右值引用的使用场景和意义左值引用的使用场景左值引用的短板用右值引用和移动语义解决上述问题移动构造移动赋值 右值引用引用左值 - std::move()ST…

【网络技术】【Kali Linux】Nmap嗅探(二)多设备扫描

上期实验博文&#xff1a;&#xff08;一&#xff09;简单扫描 一、实验环境 本次实验进行Nmap多设备扫描&#xff0c;实验使用 Kali Linux 虚拟机&#xff08;扫描端&#xff09;、Ubuntu 22.04虚拟机&#xff08;被扫描端1&#xff09;、Ubuntu 18.04虚拟机&#xff08;被扫…

C++ 动态规划 记忆化搜索 滑雪

给定一个 R 行 C 列的矩阵&#xff0c;表示一个矩形网格滑雪场。 矩阵中第 i 行第 j 列的点表示滑雪场的第 i 行第 j 列区域的高度。 一个人从滑雪场中的某个区域内出发&#xff0c;每次可以向上下左右任意一个方向滑动一个单位距离。 当然&#xff0c;一个人能够滑动到某相…

C语言----内存函数

内存函数主要用于动态分配和管理内存&#xff0c;它直接从指针的方位上进行操作&#xff0c;可以实现字节单位的操作。 其包含的头文件都是&#xff1a;string.h memcpy copy block of memory的缩写----拷贝内存块 格式&#xff1a; void *memcpy(void *dest, const void …

【buuctf--被偷走的文件】

将 ftp 流量过滤下来&#xff0c;追踪 ftp 流量&#xff0c;得到下图 先解释一下这四行什么意思&#xff1a; PASV&#xff1a; 这是FTP的命令&#xff0c;用于告知服务器在数据连接中使用被动模式&#xff08;Passive Mode&#xff09;。在被动模式下&#xff0c;数据连接的…

零基础学编程怎么入手,中文编程工具构件箱之渐变背景构件用法教程,系统化的编程视频教程上线

零基础学编程怎么入手&#xff0c;中文编程工具构件箱之渐变背景构件用法教程&#xff0c;系统化的编程视频教程上线 一、前言 今天给大家分享的中文编程开发语言工具资料如下&#xff1a; 编程入门视频教程链接 https://edu.csdn.net/course/detail/39036 编程工具及实例…

【Linux系统学习】 4.Linux实用操作 上

Linux实用操作 1.各类小技巧&#xff08;快捷键&#xff09; 1.1 ctrl c 强制停止 Linux某些程序的运行&#xff0c;如果想要强制停止它&#xff0c;可以使用快捷键ctrl c 命令输入错误&#xff0c;也可以通过快捷键ctrl c&#xff0c;退出当前输入&#xff0c;重新输入 1…

[Java][算法 哈希]Day 01---LeetCode 热题 100---01~03

LeetCode 热题 100---01~03 ------->哈希 第一题 两数之和 思路 最直接的理解就是 找出两个数的和等于目标数 这两个数可以相同 但是不能是同一个数字&#xff08;从数组上理解就是内存上不是同一位置&#xff09; 解法一&#xff1a;暴力法 暴力解万物 按照需求 …

springboot170图书电子商务网站的设计与实现

简介 【毕设源码推荐 javaweb 项目】基于springbootvue 的 适用于计算机类毕业设计&#xff0c;课程设计参考与学习用途。仅供学习参考&#xff0c; 不得用于商业或者非法用途&#xff0c;否则&#xff0c;一切后果请用户自负。 看运行截图看 第五章 第四章 获取资料方式 **项…