算法【最小生成树】

最小生成树:在无向带权图中选择择一些边,在保证联通性的情况下,边的总权值最小。

最小生成树可能不只一棵,只要保证边的总权值最小,就都是正确的最小生成树。

如果无向带权图有n个点,那么最小生成树一定有n-1条边。

扩展:最小生成树一定是最小瓶颈树(题目5)

Kruskal算法(最常用)

1.把所有的边,根据权值从小到大排序,从权值小的边开始考虑。

2.如果连接当前的边不会形成环,就选择当前的边。

3.如果连接当前的边会形成环,就不要当前的边。

4.考察完所有边之后,最小生成树的也就得到了。

证明略,其中,判断是否形成环可以使用并查集,也就是说Kruskal算法并不需要建图。

时间复杂度O(m * log m) + O(n) + O(m)

Prim算法(不算常用)

1.解锁的点的集合叫set(普通集合)、解锁的边的集合叫heap(小根堆)。set和heap都为空。

2.可从任意点开始,开始点加入到set,开始点的所有边加入到heap。

3.从heap中弹出权值最小的边e,查看边e所去往的点x

   A.如果x已经在set中,边e舍弃,重复步骤3。

   B.如果x不在set中,边e属于最小生成树,把x加入set,重复步骤3。

4.当heap为空,最小生成树的也就得到了。

证明略!

时间复杂度O(n + m) + O(m * log m)

Prim算法的优化(比较难,不感兴趣可以跳过)请一定要对堆很熟悉

1.小根堆里放(节点,到达节点的花费),根据到达节点的花费来组织小根堆。

2.小根堆弹出(u节点,到达u节点的花费y),y累加到总权重上去,然后考察u出发的每一条边

  假设,u出发的边,去往v节点,权重w

  A.如果v已经弹出过了(发现过),忽略该边。

  B.如果v从来没有进入过堆,向堆里加入记录(v, w)。

  C. 如果v在堆里,且记录为(v, x)。

     1)如果w < x,则记录更新成(v, w),然后调整该记录在堆中的位置(维持小根堆)。

     2)如果w >= x,忽略该边。

3.重复步骤2,直到小根堆为空。

时间复杂度O(n+m) + O((m+n) * log n)

下面通过一些题目加深对最小生成树的理解。

题目一

测试链接:https://www.luogu.com.cn/problem/P3366

分析:这个就是一个最小生成树模板代码。代码如下。

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int N, M;
int father[200002] = {0};
vector<vector<int>> b;
int find(int i){if(i != father[i]){father[i] = find(father[i]);}return father[i];
}
bool Union(int a, int b){int behalf_a = find(a);int behalf_b = find(b);if(behalf_a != behalf_b){father[behalf_a] = behalf_b;return true;}else{return false;}
}
int main(void){int temp1, temp2, temp3;int ans = 0;int num = 0;scanf("%d%d", &N, &M);for(int i = 0;i < M;++i){vector<int> temp;b.push_back(temp);scanf("%d%d%d", &temp1, &temp2, &temp3);b[i].push_back(temp1);b[i].push_back(temp2);b[i].push_back(temp3);}sort(b.begin(), b.end(), [](vector<int> v1, vector<int> v2)->bool{return v1[2] < v2[2];});for(int i = 1;i <= N;++i){father[i] = i;}for(int i = 0;i < M;++i){if(Union(b[i][0], b[i][1])){ans += b[i][2];++num;}}if(num == N-1){printf("%d", ans);}else{printf("orz");}
}

其中,采用并查集可以知道两个节点是否属于同一个集合,故不需要建图。

题目二

测试链接:https://www.luogu.com.cn/problem/P3366

分析:这和题目一一样,不过题目一采用Kruskal算法,题目二采用Prim算法。代码如下。

#include <iostream>
#include <vector>
#include <queue>
using namespace std;
vector<vector<vector<int>>> graph;
vector<bool> visited;
int main(void){int N, M, ans = 0;int temp1, temp2, temp3;int num = 0;scanf("%d%d", &N, &M);visited.assign(N+1, false);vector<vector<int>> temp;for(int i = 0;i <= N;++i){graph.push_back(temp);}for(int i = 0;i < M;++i){scanf("%d%d%d", &temp1, &temp2, &temp3);vector<int> tmp1;tmp1.push_back(temp2);tmp1.push_back(temp3);graph[temp1].push_back(tmp1);vector<int> tmp2;tmp2.push_back(temp1);tmp2.push_back(temp3);graph[temp2].push_back(tmp2);}visited[1] = true;++num;auto cmp = [](vector<int> v1, vector<int> v2)->bool{return v1[1] > v2[1];};priority_queue<vector<int>, vector<vector<int>>, decltype(cmp)> q(cmp);for(int i = 0;i < graph[1].size();++i){q.push(graph[1][i]);}while (!q.empty()){int cur = (q.top())[0];int weigth = (q.top())[1];q.pop();if(visited[cur] == false){ans += weigth;visited[cur] = true;++num;for(int i = 0;i < graph[cur].size();++i){q.push(graph[cur][i]);}}}if(num == N){printf("%d", ans);}else{printf("orz");}
}

其中,采用邻接表方式建图;使用优先队列辅助。

题目三

测试链接:https://leetcode.cn/problems/checking-existence-of-edge-length-limited-paths/

分析:可以先将queries数组按limit从小到大排序,将边数组按权值从小到大排序。这样在生成最小生成树的时候,遍历边数组,对于每一个queries,如果边的权值大于limit,则停止,查询queries的两个节点是否在同一个集合,在则为true,不在则为false。代码如下。

class Solution {
public:vector<int> father;int find(int i){if(i != father[i]){father[i] = find(father[i]);}return father[i];}void Union(int a, int b){int behalf_a = find(a);int behalf_b = find(b);if(behalf_a != behalf_b){father[behalf_a] = behalf_b;}}void build(int n){for(int i = 0;i < n;++i){father[i] = i;}}vector<bool> distanceLimitedPathsExist(int n, vector<vector<int>>& edgeList, vector<vector<int>>& queries) {vector<bool> ans;father.assign(n, 0);sort(edgeList.begin(), edgeList.end(), [](vector<int> v1, vector<int> v2)->bool{return v1[2] < v2[2];});int legnth1 = edgeList.size();int length2 = queries.size();ans.assign(length2, false);for(int i = 0;i < length2;++i){queries[i].push_back(i);}sort(queries.begin(), queries.end(), [](vector<int> v1, vector<int> v2)->bool{return v1[2] < v2[2];});build(n);for(int i = 0, j = 0;i < length2;++i){for(;j < legnth1 && edgeList[j][2] < queries[i][2];++j){Union(edgeList[j][0], edgeList[j][1]);}ans[queries[i][3]] = (find(queries[i][0]) == find(queries[i][1]));}return ans;}
};

其中,采用Kruskal算法生成最小生成树。

题目四

测试链接:https://www.luogu.com.cn/problem/P2330

分析:题目挺花里胡哨的,其实就是一个最小生成树。代码如下。

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int n, m;
vector<vector<int>> road;
int father[301] = {0};
int find(int i){if(i != father[i]){father[i] = find(father[i]);}return father[i];
}
bool Union(int a, int b){int behalf_a = find(a);int behalf_b = find(b);if(behalf_a != behalf_b){father[behalf_a] = behalf_b;return true;}else{return false;}
}
int main(void){int u, v, c;int s = 0, max_score = 0;scanf("%d%d", &n, &m);for(int i = 0;i < m;++i){scanf("%d%d%d", &u, &v, &c);vector<int> temp;temp.push_back(u);temp.push_back(v);temp.push_back(c);road.push_back(temp);}for(int i = 1;i <= n;++i){father[i] = i;}sort(road.begin(), road.end(), [](vector<int> v1, vector<int> v2)->bool{return v1[2] < v2[2];});for(int i = 0;i < m;++i){if(Union(road[i][0], road[i][1])){++s;max_score = road[i][2];}}printf("%d %d", s, max_score);
}

其中,主体和题目一差不多,只是求的东西不一样。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SVGJS操作

svgjs用于操作 SVG 和动画的轻量级库。 官网 SVG.js v3.2 |家 (svgjs.dev) 效果 代码如下 <template><h3>测试操作已有SVG</h3><button click"changeText()">利用ID定位</button><button click"changeChild()">chan…

【Hot100】LeetCode—322. 零钱兑换

目录 1- 思路动态规划 2- 实现⭐322. 零钱兑换——题解思路 3- ACM 实现 原题链接&#xff1a;322. 零钱兑换 1- 思路 动态规划 动规五部曲 1- 定义 dp 数组确定含义 dp[j] 代表凑到金钱为 j 的最少硬币个数 2- 递推公式 dp[j] Math.min(dp[j],dp[amount-]1) 3- 初始化 dp[…

【苍穹外卖】前端 Day 1

1 Vue 1.1 通过 vue cli 脚手架创建前端工程 1.2 项目结构 1.3 启动项目 VS Code 启动前端项目&#xff1a; npm run serve 注意这里占用端口号 8080 与 java springboot 占用端口号一致&#xff0c;有冲突 serve 是这个名字 终止&#xff1a;ctrl c 修改端口号 2 vue 基本…

信刻光盘安全隔离与信息交换系统

随着各种数据传输、储存技术、信息技术的快速发展&#xff0c;保护信息安全是重中之重。军工、政府、部队及企事业单位等利用A网与B网开展相关工作已成为不可逆转的趋势。针对于业务需要与保密规范相关要求&#xff0c;涉及重要秘密信息&#xff0c;需做到安全的物理隔离&#…

C#中常用的5个访问修饰符

在C#中&#xff0c;访问修饰符&#xff08;Access Modifiers&#xff09;用于定义类、方法、属性、变量等成员的可访问性范围。它们决定了哪些代码能够访问特定的成员。以下是C#中最常用的五个访问修饰符&#xff1a; public public 修饰符表示该成员是公开的&#xff0c;可以被…

离线版问卷-可集成到现有系统

目录标题 离线版问卷&#x1f4a1;前言亮点场景题外话 &#x1f3a8; 预览&#x1f308; 技术栈&#x1f4e6; 仓库&#x1f4bb; 初始化&#x1f680; 启动&#x1f6e0;️ 打包&#x1f5c2; 目录结构✨ 使用方法集成【设计问卷】集成【填写问卷】集成【只读问卷】集成【填答…

省委书记邀约大学生创业,长沙又一次为年轻人沸腾

敢想敢做的大学生&#xff0c;一直是创新创业的主力军。尤其是这些年“学术型”创业团队在各行各业越来越多见&#xff0c;市场对他们的接纳和支持力度也越来越强&#xff0c;给了新一代的大学生们更大的底气。 以往&#xff0c;大学生创业经常“落地生根”&#xff0c;先搞事…

【编译原理】编译器概述、编译器结构、编译器实例

编译器概述、编译器结构、编译器实例 编译器概述 1.编译器是一个程序 核心功能是把源代码翻译成目标代码 比如源代码&#xff1a;C/C&#xff0c;Java&#xff0c;C#&#xff0c;html 目标代码&#xff1a;X86&#xff0c;IA64,ARM,… 把一种源程序翻译成另外一种源程序&…

Facebook的秘密算法:如何提升你的社交体验

在数字时代&#xff0c;社交媒体平台已经成为我们日常生活的重要组成部分。作为全球最大的社交网络之一&#xff0c;Facebook通过其复杂的算法&#xff0c;影响着亿万用户的社交体验。这些算法不仅决定了我们在平台上看到的内容&#xff0c;还在背后默默优化我们的互动方式。本…

[数据集][目标检测]汽车头部尾部检测数据集VOC+YOLO格式5319张3类别

数据集制作单位&#xff1a;未来自主研究中心(FIRC) 版权单位&#xff1a;未来自主研究中心(FIRC) 版权声明&#xff1a;数据集仅仅供个人使用&#xff0c;不得在未授权情况下挂淘宝、咸鱼等交易网站公开售卖,由此引发的法律责任需自行承担 数据集格式&#xff1a;Pascal VOC格…

SpringSecurity剖析

1、SpringSecurity 入门 1.1、简介 Spring Security是一个功能强大且高度可定制的身份验证和访问控制框架。它是用于保护基于Spring的应用程序的实际标准。Spring Security是一个框架&#xff0c;致力于为Java应用程序提供身份验证和授权。与所有Spring项目一样&#xff0c;Sp…

红帽RHCE认证值不值得考?RHCE认证有什么用?

在IT行业&#xff0c;红帽认证作为一项衡量Linux技能水平的重要标准&#xff0c;受到了广泛的关注和认可。 拥有一张权威认证证书无疑是提升自身竞争力、实现职业发展的重要途径。 RHCE认证作为Linux领域的顶级认证之一&#xff0c;其价值和意义不言而喻。 那么&#xff0c;…

工作中遇到了一个线程中断异常

工作中遇到了一个线程中断异常&#xff0c;备忘记录一下。 主线程使用CompletableFuture开辟子线程进行了并行查询&#xff0c;但其中某个子线程执行时发生异常&#xff0c;没有捕获。 结果就在主线程等待所有子线程都执行完毕时报ExecutionException 从而对主线程打上了中断标…

PowerBi 柱形图,数据标签无法显示在端外

如图 即使设置了“数据标签”显示“端外“&#xff0c;仍然不作用。 原因其实是因为Y轴的数据范围设置不当&#xff0c;如图&#xff0c;当前Y轴范围是0到自动 只需要修改为最大和最小值都是自动即可&#xff0c;选中0 按backspace键删除&#xff0c;然后&#xff0c;鼠标在任意…

机器学习-------数据标准化

什么是归一化&#xff0c;它与标准化的区别是什么&#xff1f; 一 作用 在做训练时&#xff0c;需要先将特征值与标签标准化&#xff0c;可以防止梯度防炸和过拟合&#xff1b; 将标签标准化后&#xff0c;网络预测出的数据是符合标准正态分布的—StandarScaler()&#xff0c…

排班系统|基于Springboot+vue的医护人员排班系统(源码+数据库+文档)

排班系统|医护人员排班系统 目录 基于Springbootvue的医护人员排班系统 一、前言 二、系统设计 三、系统功能设计 医护类型管理 排班类型管理 科室信息管理 医护信息管理 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&…

CSS3 var() 函数:解锁动态样式与高效维护的密钥

在CSS3中&#xff0c;var()函数是一个强大的特性&#xff0c;它允许我们在样式表中定义可重用的值&#xff0c;并在多个地方引用它们。这种机制不仅提高了代码的可维护性和灵活性&#xff0c;还使得动态更新样式成为可能。本文将深入解析CSS3中的var()函数&#xff0c;包括其用…

C语言代码练习(第十八天)

今日练习&#xff1a; 48、猴子吃桃问题。猴子第1天摘下若干个桃子&#xff0c;当即吃了一半&#xff0c;还不过瘾&#xff0c;又多吃了一个。第2天早上又将剩下的桃子吃掉一半&#xff0c;又多吃了一个。以后每天早上都吃了前一天剩下的一半零一个。到第10天早上想再吃时&…

【Python篇】PyQt5 超详细教程——由入门到精通(中篇二)

文章目录 PyQt5超详细教程前言第7部分&#xff1a;生成图表与数据可视化7.1 matplotlib 与 PyQt5 的结合7.2 在 PyQt5 中嵌入 matplotlib 图表示例 1&#xff1a;嵌入简单的 matplotlib 图表代码详解&#xff1a; 7.3 动态生成图表示例 2&#xff1a;动态更新图表代码详解&…

Typora激活脚本

参考文章1 图文教程 | 2024Typora最新版免费激活使用教程&#xff08;新旧版可用&#xff09;_typora激活-CSDN博客 参考文章2 解决Typora闪退、文件打不开等问题——Typora免费使用教程&#xff08;也有免费版的&#xff09;_为什么点击typora没反应-CSDN博客 下面是脚本: …