【代码随想录训练营第42期 Day57打卡 - 图论Part7 - Prim算法与Kruskal算法

server/2024/9/20 7:19:37/ 标签: 算法, 图论, Prim算法

目录

一、Prim算法

二、题目与题解

题目:卡码网 53. 寻宝

题目链接

题解1:Prim算法

题解2:Prim算法优化 

题解3:Kruskal算法

 三、小结

一、Prim算法与Kruskal算法

Prim算法是一种贪心算法,用于求解加权无向图的最小生成树问题。其中,最小生成树是指一个边的子集,它连接图中的所有顶点,且边的总权重最小,并且没有形成环。

Kruskal算法是一种用于寻找加权无向图的最小生成树的贪心算法。所谓最小生成树,是指在保证图中的所有顶点都通过边相连的前提下,所有边的权重之和最小的树形结构。

对于Prim算法和Kruskal算法的简单了解,这里推荐看看:【图-最小生成树-Prim(普里姆)算法和Kruskal(克鲁斯卡尔)算法

二、题目与题解

题目:卡码网 53. 寻宝

题目链接

53. 寻宝(第七期模拟笔试) (kamacoder.com)

题目描述

在世界的某个区域,有一些分散的神秘岛屿,每个岛屿上都有一种珍稀的资源或者宝藏。国王打算在这些岛屿上建公路,方便运输。

不同岛屿之间,路途距离不同,国王希望你可以规划建公路的方案,如何可以以最短的总公路距离将 所有岛屿联通起来(注意:这是一个无向图)。 

给定一张地图,其中包括了所有的岛屿,以及它们之间的距离。以最小化公路建设长度,确保可以链接到所有岛屿。

输入描述

第一行包含两个整数V 和 E,V代表顶点数,E代表边数 。顶点编号是从1到V。例如:V=2,一个有两个顶点,分别是1和2。

接下来共有 E 行,每行三个整数 v1,v2 和 val,v1 和 v2 为边的起点和终点,val代表边的权值。

输出描述

输出联通所有岛屿的最小路径总距离

输入示例

7 11
1 2 1
1 3 1
1 5 2
2 6 1
2 4 2
2 3 2
3 4 1
4 5 1
5 6 2
5 7 1
6 7 1

输出示例

6

提示信息

数据范围:
2 <= V <= 10000;
1 <= E <= 100000;
0 <= val <= 10000;

如下图,可见将所有的顶点都访问一遍,总距离最低是6.

  

题解1:Prim算法

建议先看视频了解了Prim算法的实现步骤再看以下思路和步骤。

基本思路:

1.选择一个起始顶点,将其标记为已访问,并将其距离设置为0,其他顶点的距离设置为无穷大

2.创建一个循环,直到所有顶点都被访问:

        初始化一个变量来记录当前最小边的权重,将其设置为无穷大。

        初始化两个变量来记录最小边的两个顶点。

3.遍历所有已访问顶点,对于每个已访问顶点,遍历它的所有邻接顶点:

        如果邻接顶点未被访问,并且连接这两个顶点的边的权重小于当前记录的最小边权重,则更新最小边权重和对应的两个顶点。

        将找到的最小边加入到最小生成树中,并标记连接的非最小生成树顶点为已访问。 更新该顶点的所有邻接顶点到最小生成树的最短距离。

4.当所有顶点都被访问后,算法结束,此时最小生成树由记录的所有边组成。

#include <bits/stdc++.h>
using namespace std;int main()
{int v, e;int x, y, k;cin >> v >> e;                                              // 输入顶点数v(注意范围是:1 - v)和边数evector<vector<int>> grid(v + 1, vector<int>(v + 1, 10001)); // 二维数组grid,用于存储图的邻接矩阵,初始值为10001(表示无穷大)while (e--){cin >> x >> y >> k; // 输入边的两个顶点和权值// 因为是双向图,所以两个方向都要填上grid[x][y] = k;grid[y][x] = k;}// 用于存储每个顶点到最小生成树的最短距离 -- 由于顶点数为v,这里大小设置为v+1vector<int> minDist(v + 1, 10001);// 初始化第一个顶点到最小生成树的距离为0minDist[1] = 0;// 用于标记顶点是否已经在最小生成树中vector<bool> isInTree(v + 1, false);// 我们只需要循环 n-1次,因为最小生成树有v-1条边,这样就可以把n个节点的图连在一起for (int i = 1; i < v; i++){// 选择距离最小生成树最近的顶点int cur = -1;                // 当前选中的顶点 -- 最终选中的cur节点即是加入最小生成树的最近节点int minVal = INT_MAX;        // 当前最小距离,初始化为无穷大for (int j = 1; j <= v; j++) // 顶点编号:1 - v{if (!isInTree[j] && minDist[j] < minVal) // 选择不在最小生成树中且距离最小的顶点{minVal = minDist[j];cur = j;}}// 最近节点(cur)加入生成树isInTree[cur] = true;// 更新非生成树节点到生成树的距离(即更新minDist数组):由于新加入了cur节点,这里只需要多考虑cur与不在最小生成树的节点的距离即可for (int j = 1; j <= v; j++){if (!isInTree[j] && grid[cur][j] < minDist[j]) // 如果顶点j不在生成树中,并且通过cur到j的距离小于当前记录的最短距离,则更新{minDist[j] = grid[cur][j];}}}int ans = 0;                 // 统计结果for (int i = 2; i <= v; i++) // 累加每个顶点到生成树的最短距离,注意从第二个顶点开始累加,因为第一个顶点距离为0{ans += minDist[i];}cout << ans << endl;
}
题解2:Prim算法优化 

这题还可以用优先队列(堆)进行优化,这也是Prim算法最经典的使用方法:

算法思路:

PRIM(G, w, s):
1. for each u in G.V:
     u.key = INFINITY
     u.pi = NIL
2. s.key = 0
3. Q = G.V (创建一个顶点的优先队列,初始时包含所有顶点)
4. while Q is not empty:
     u = EXTRACT-MIN(Q) (从Q中取出具有最小key值的顶点)
     for each v in G.Adj[u]: (遍历顶点u的所有邻接顶点v)
         if v in Q and w(u, v) < v.key:
             v.pi = u
             v.key = w(u, v)

其中:G表示图,w表示边的权重函数,s是起始顶点。u.key表示顶点u到最小生成树的最短边权重,u.pi表示在最小生成树中顶点u的前驱顶点。 

代码如下:

#include <bits/stdc++.h>
using namespace std;int main()
{int v, e;cin >> v >> e; // 输入顶点数v和边数e// 使用邻接矩阵存储图的边信息,初始化为无穷大vector<vector<int>> grid(v + 1, vector<int>(v + 1, INT_MAX));// 读取边的信息for (int i = 1; i <= e; ++i){int x, y, k;cin >> x >> y >> k; // 输入边的两个顶点和权值grid[x][y] = k;grid[y][x] = k;}// 使用优先队列来存储顶点及其到最小生成树的最短距离priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;// 用于存储每个顶点到最小生成树的最短距离,初始化为无穷大vector<int> minDist(v + 1, INT_MAX);minDist[1] = 0; // 第一个顶点到最小生成树的距离为0// 标记顶点是否已经在最小生成树中vector<bool> isInTree(v + 1, false);// 将第一个顶点加入优先队列pq.push({0, 1});while (!pq.empty()){// 从优先队列中取出距离最小生成树最近的顶点int cur = pq.top().second;pq.pop();// 如果该顶点已经在最小生成树中,则跳过if (isInTree[cur])continue;// 将该顶点标记为已加入最小生成树isInTree[cur] = true;// 更新邻接顶点到最小生成树的最短距离for (int j = 1; j <= v; ++j){if (!isInTree[j] && grid[cur][j] < minDist[j]){minDist[j] = grid[cur][j];pq.push({minDist[j], j}); // 将更新后的顶点和距离加入优先队列}}}// 计算最小生成树的总权重int ans = 0;for (int i = 2; i <= v; ++i){ans += minDist[i];}cout << ans << endl; // 输出最小生成树的总权重return 0;
}
题解3:Kruskal算法

对应Kruskal算法,一般按照以下思路进行:

KRUSKAL(G)算法思路:
1. 将G中的所有边按权重从小到大排序。
2. 初始化森林F,使得每个顶点都是一个独立的树。
3. for each 边(u, v) in G的边列表,按权重从小到大:
     a. 查找u所在的树的根节点。
     b. 查找v所在的树的根节点。
     c. if u和v的根节点不同(即不会形成环):
          i. 将边(u, v)加入森林F。
          ii. 合并u和v所在的树。
4. 返回森林F,即为最小生成树。

其中某些步骤的实现即是前缀和的几个基本操作,之前有提到,这里不多做解释。

代码如下: 

#include <bits/stdc++.h>
using namespace std;int n = 1001;
vector<int> father(n, 0); // 并查集数组:节点编号是从1开始的// l,r为 边两边的节点,val为边的数值
struct Edge
{int l, r, val;
};// 并查集初始化
void init()
{for (int i = 1; i <= n; i++)father[i] = i;
}// 并查集里寻找该节点的根节点(带路径压缩)
int find(int u)
{if (u != father[u])father[u] = find(father[u]);return father[u];
}// join 函数用于合并两个节点所在的集合:将v-u这条边加入并查集
void join(int u, int v)
{int rootu = find(u);int rootv = find(v);if (rootu != rootv)father[rootv] = rootu;
}int main()
{int v, e;int v1, v2, val;vector<Edge> edges; // edges数组存放所有边 -- 每个元素都是Edge结构(l,r,val)int ans = 0;cin >> v >> e;while (e--){cin >> v1 >> v2 >> val;edges.push_back({v1, v2, val});}// Kruskal算法// 1.按边的权值对边进行从小到大排序sort(edges.begin(), edges.end(), [](const Edge &a, const Edge &b){ return a.val < b.val; });// 2.并查集初始化init();for (Edge edge : edges) // 3.从头开始遍历边:按边的权重从小到大{// 4.并查集,搜出两个节点u和v所在树的根节点 -- 确保不会形成环int rootu = find(edge.l);int rootv = find(edge.r);// 5.如果u,v根节点不同 -- 即不会形成环(注意)if (rootu != rootv){ans += edge.val;    // 6.这条边u-v加入生成树join(rootu, rootv); // 7.合并u,v所在的树:两个节点加入到同一个集合}}cout << ans << endl;return 0;
}

 三、小结

至此完善了这天的打卡,后边会继续加油!


http://www.ppmy.cn/server/117778.html

相关文章

构建高效 Python Web API:RESTful 设计与 GraphQL 实践

构建高效 Python Web API&#xff1a;RESTful 设计与 GraphQL 实践 在现代 Web 开发中&#xff0c;API 是前后端通信的核心枢纽&#xff0c;设计一个高效且易于扩展的 API 是保证系统良好运行的基础。本文详细探讨 RESTful API 的设计准则&#xff0c;如何生成 API 文档&#…

HCIP--<OSPF2>

目录 一&#xff0c;OSPF的不规则区域 1&#xff09;远离骨干区域的非骨干区域 2&#xff09;不连续骨干区域(和上面一样) 二&#xff0c;OSPF数据库表 三。优化OSPF的LSA&#xff08;缺少LSA的更新量&#xff09; [1]手工汇总&#xff1a;减少骨干区域的LSA [2]特殊区域&…

轨道列车舱门检测系统源码分享

轨道列车舱门检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer…

GitHub图床

GitHub图床 文章目录 GitHub图床图床介绍Github访问GitHub手动修改hostsgithub520 加速器创建账户创建仓库创建token PicGoTypora 图床介绍 图床 存放图片的地方 为什么设置图床呢 在我认识图床之前, 有一个问题 [^放在typora上面的图片, 其实是一个链接, 并且将图片存放在本地…

TS - tsconfig.json 和 tsconfig.node.json 的关系,如何在TS 中使用 JS 不报错

目录 1&#xff0c;前言2&#xff0c;二者关系2.1&#xff0c;使用 3&#xff0c;遇到的问题3.1&#xff0c;TS 中使用 JS 1&#xff0c;前言 通过 Vite 创建的 Vue3 TS 项目&#xff0c;根目录下会有 tsconfig.json 和 tsconfig.node.json 文件&#xff0c;并且存在引用关系…

51单片机+proteus+实验(I2C和蜂鸣器)

目录 1.蜂鸣器 1.1基本概念 1.1.1蜂鸣器的简介 1.1.2蜂鸣器的硬件原理 1.1.3蜂鸣器的音色 1.2代码 1.2.1不同音色驱动 1.2.2使用Music Encode1软件来生成音乐 1.3proteus仿真 2.I2C 2.1基本概念 2.1.1 I2C的基本概念 2.1.2 I2C的通讯时序 2.1.3AT24C02数据帧 ​编…

Qt_显示类控件

目录 一、QLabel 1、QLabel属性介绍 2、textFormat文本格式 3、pixmap标签图片 3.1 resizeEvent 4、QFrame边框 5、alignment文本对齐 6、wordWrap自动换行 7、indent设置缩进 8、margin设置边距 9、buddy设置伙伴 二、QLCDNumber 1、QLCDNumber属性介绍 2、实…

【AI大模型】ChatGPT模型原理介绍(下)

目录 &#x1f354; GPT-3介绍 1.1 GPT-3模型架构 1.2 GPT-3训练核心思想 1.3 GPT-3数据集 1.4 GPT-3模型的特点 1.5 GPT-3模型总结 &#x1f354; ChatGPT介绍 2.1 ChatGPT原理 2.2 什么是强化学习 2.3 ChatGPT强化学习步骤 2.4 监督调优模型 2.5 训练奖励模型 2.…

上海亚商投顾:沪指探底回升 华为产业链午后爆发

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 一.市场情绪 沪指昨日探底回升&#xff0c;深成指、创业板指盘中跌逾1%&#xff0c;午后集体拉升翻红。华为产业链午后走强…

朴朴超市 签到 任务脚本

脚本主要用于自动化处理朴朴超市APP的签到和组队任务。以下是对脚本中主要方法的作用解析: 初始化和登录 - __init__: - 初始化类对象,设置用户信息和请求头,尝试获取并设置随机位置。 - get_AccessToken: - 使用`refresh_token`刷新并获取`access_token`,用于后续的A…

安卓玩机工具-----ADB与 FASTBOOT模式 图形化 多功能玩机刷机工具

工具说明 这款工具是英文版。易于使用的工具提供了用于运行 ADB 和 Fastboot 命令的图形用户界面。ADB 功能包括旁加载、安装和卸载应用程序、测试设备以及重新启动到不同的模式。可以使用 fastboot 命令进行设备管理;其中包括检查 Antirollback 和 active slots 等变…

Nginx:高性能的Web服务器与反向代理

在当今的互联网世界中&#xff0c;Web服务器的选择对于网站的性能、稳定性和安全性至关重要。Nginx&#xff08;发音为“engine X”&#xff09;凭借其卓越的性能、丰富的功能集和灵活的配置选项&#xff0c;成为了众多网站和应用程序的首选Web服务器和反向代理。本文将深入探讨…

Canal+RabbitMQ数据同步环境配置

Canal 是阿里巴巴开发的开源工具&#xff0c;主要用于解析 MySQL 的 binlog 日志&#xff0c;从而实现数据同步。Canal 会模拟 MySQL 从库的协议&#xff0c;订阅主库的 binlog&#xff0c;从而获取数据库的变更信息。 将 Canal 解析到的 MySQL 数据库变更消息通过 RabbitMQ 分…

详细分析Uniapp中的轮播图基本知识(附Demo)

目录 前言1. 基本知识2. Demo2.1 基本2.2 自定义分页2.3 自定义动画 3. 扩展 前言 先看代码示例&#xff1a; 实现了一个带有分页指示器的轮播图组件 <template><view class"work-container"><!-- 轮播图 --><uni-swiper-dot class"uni…

flask框架

Flask 1 flask简介 我们之所以在浏览器中输入localhost:8080然后就可以把webapps下面的项目文件以浏览器的方式打开&#xff0c;功臣在与tomcat。python语言写的项目&#xff0c;转换为web&#xff0c;Flask框架 轻量级web应用框架。 环境准备&#xff1a; pip install fl…

SSHamble:一款针对SSH技术安全的研究与分析工具

关于SSHamble SSHamble是一款功能强大的SSH技术安全分析与研究工具&#xff0c;该工具基于Go语言开发&#xff0c;可以帮助广大研究人员更好地分析SSH相关的安全技术与缺陷问题。 功能介绍 SSHamble 是用于 SSH 实现的研究工具&#xff0c;其中包含下列功能&#xff1a; 1、针…

React源码学习(一):如何学习React源码

本系列源码学习&#xff0c;是基于 v16.13.1&#xff0c;v17.x与v16.x区别并不太大&#xff01; 一、如何正确的学习React源码&#xff1f; 找到Github&#xff0c;转到React仓库&#xff0c;fork / clone源码&#xff1a;React 查看Readme&#xff0c;在Documentation中有Cont…

Gateway学习笔记

目录 介绍&#xff1a; 核心概念 依赖 路由 断言 基本的断言工厂 自定义断言 过滤器 路由过滤器 过滤器工厂 自定义路由过滤器 全局过滤器 其他 过滤器执行顺序 前置后置&#xff08;&#xff1f;&#xff09; 跨域问题 yaml 解决 配置类解决 介绍&#x…

鸿蒙 ArkUI组件一

ArkUI组件 布局 布局指用特定的组件或者属性来管理用户页面所放置UI组件的大小和位置。在实际的开发过程中&#xff0c;需要遵守以下流程保证整体的布局效果&#xff1a; 确定页面的布局结构。分析页面中的元素构成。选用适合的布局容器组件或属性控制页面中各个元素的位置和大…

适合博客的组件库

在选择适合博客的组件库时&#xff0c;需要考虑博客的主题、内容类型以及预期的用户体验。以下是一些推荐的组件库&#xff0c;它们各自具有独特的特点和优势&#xff0c;能够帮助你提升博客的视觉效果和用户体验&#xff1a; React Markdown&#xff1a;非常适合技术博客和教…