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

news2024/11/16 13:28:21

目录

一、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)和边数e
    vector<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+1
    vector<int> minDist(v + 1, 10001);
    // 初始化第一个顶点到最小生成树的距离为0
    minDist[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.coloradmin.cn/o/2133802.html

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

相关文章

差分约束---将不等式转换为图的算法

概念&#xff1a;已知一个差分约束系统&#xff08;差分约束系统即一种特殊的n元一次不等式组&#xff09;&#xff0c;形如,要求求出是否存在一组解使得所有约束条件满足。由于在最短路中与该不等式形式相似&#xff0c;因此&#xff0c;可以利用图论&#xff0c;从对应的j点连…

探索Web3前沿:革新性算力共享平台,重塑数字资源利用新时代

基于Web3的算力共享平台 随着区块链技术的不断发展和Web3.0时代的到来,算力共享平台逐渐成为推动数字经济和人工智能发展的重要力量。基于Web3的算力共享平台通过去中心化、分布式网络等技术手段,实现了算力的高效、透明和安全的共享,为人工智能、科学研究、艺术创作等多个…

Kamailio-基于Homer与heplify的SIP信令监控-1

接上篇Kamailio监控&#xff0c;对Kamailio的一个基础监控有了一定的概念&#xff0c;但是光看数字如果发现问题&#xff0c;要如何回顾解决呢&#xff1f;生产环境不能随时随地抓包来确定链路的正常与否。 这个时候 Sipcapture 公司推出了Homer这个开源软件&#xff0c;目前G…

【软件测试】常用的开发、测试模型

哈喽&#xff0c;哈喽&#xff0c;大家好~ 我是你们的老朋友&#xff1a;保护小周ღ 今天给大家带来的是 【软件测试】常用的开发、测试模型&#xff0c;首先了解, 什么是软件的生命周期, 测试的生命周期, 常见的开发模型: 瀑布, 螺旋, 增量, 迭代, 敏捷. 常用的测试模型, …

银河麒麟桌面系统卸载应用报错快速解决

银河麒麟桌面系统卸载应用报错快速解决 1、问题简述2、解决方案步骤 1: 删除dpkg信息步骤 2: 强制卸载步骤 3: 验证与清理 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 1、问题简述 在银河麒麟桌面系统中卸载应用时&#xff0c;可能会遇…

AE 让合成重复循环播放

在合成上点右键 > Time > Enable Time Remapping 按住 Alt 键&#xff0c;点秒表图标 输入 loop_out("cycle", 0) 将子合成拖到此合成结束的位置 结束

Linux 上自动下载 Docker 依赖并离线安装的完整指南

Linux 上自动下载 Docker 依赖并离线安装的完整指南 文章目录 Linux 上自动下载 Docker 依赖并离线安装的完整指南一 添加软件源二 更新 yum 缓存三 下载依赖四 打包 rpm 文件五 离线安装 这篇指南详细讲解了如何在CentOS 7.9系统上&#xff0c;通过 yum命令自动下载Docker的所…

vue3使用leaflet+trackplayer实现非地图动画轨迹(市场平面图动态轨迹)

vue3使用leaflettrackplayer实现非地图动画轨迹(市场平面图动态轨迹) 先下载 leaflet 和 leaflet-trackplayer两个主要库 leaflet官方文档 npm install leaflet npm install leaflet-trackplayer然后在页面中引用 html <template><button click"playMap&quo…

LLM - 理解 多模态大语言模型 (MLLM) 的指令微调与相关技术 (四)

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/142063880 免责声明&#xff1a;本文来源于个人知识与公开资料&#xff0c;仅用于学术交流&#xff0c;欢迎讨论&#xff0c;不支持转载。 完备(F…

第 12 篇 Helm 部署 Redis

文章目录 Redis Chart部署 Redis 单机版部署 Redis 哨兵版第 1 步&#xff1a;准备 values.yaml 配置文件第 2 步&#xff1a;安装 bitnami/redis第 3 步&#xff1a;解决 Pod Pending 问题查看 Pod 状态添加 PV 持久化卷创建 pv1创建 pv2创建 pv3 查看 Pod 状态 第 4 步&#…

java实际开发——数据库存储金额时用什么数据类型?(MySQL、PostgreSQL)

目录 java开发时金额用的数据类型——BigDecimal MySQL存储金额数据时用的数据类型是——decimal PostgreSQL存储金额数据时用的数据类型是——decimal 或 money java开发时金额用的数据类型——BigDecimal https://blog.csdn.net/Jilit_jilit/article/details/142180903?…

传统Malmquist-Luenberger指数与全局Malmquist-Luenberger指数的区别

1.全局技术前沿的构建 1.1传统ML指数 技术前沿的时间依赖性 传统的Malmquist-Luenberger&#xff08;ML&#xff09;指数在每个时期&#xff08;例如年份&#xff09;单独构建各自的技术前沿。这意味着每个时期的生产可能性集合和技术效率都是基于该时期的数据。 不可比性问…

【包教包会】CocosCreator3.xSprite和Label渐变色(支持3.x、支持原生、可合批)

完美适配Web、原生平台&#xff08;其余平台没测过&#xff09;。 下载地址&#xff1a;水煮肉片饭/Palette3.x 如何导入自己项目&#xff1a; 1、将Demo中Palette.ts复制到自己项目assets目录下 2、新建一个Sprite或Label节点&#xff0c;将Palette组件挂上去 3、设置顶点…

openstack之glance介绍

概念 glance为nova提供镜像服务&#xff0c;用于启动实例&#xff0c;预建镜像已安装cloud-init&#xff0c;可以访问openstack基金会获取操作系统镜像&#xff1a;官方镜像 格式 raw&#xff1a;无格式的镜像&#xff1b; vhd&#xff1a;hyper-v使用的格式&#xff1b; vm…

arcgisPro地理配准

1、添加图像 2、在【影像】选项卡中&#xff0c;点击【地理配准】 3、 点击添加控制点 4、选择影像左上角格点&#xff0c;然后右击填入目标点的投影坐标 5、依次输入四个格角点的坐标 6、点击【变换】按钮&#xff0c;选择【一阶多项式&#xff08;仿射&#xff09;】变换 7…

数据结构(7.3_1)——二叉排序树

二叉排序树&#xff0c;又称二叉查找树(BST,Binary Search Tree) 一棵二叉树或者是空二叉树&#xff0c;或者是具有如下性质的二叉树&#xff1a; 左子树上所有结点的关键字均小于根结点的关键字&#xff1b;右子树上所有结点的关键字均大于根结点的关键字&#xff1b;左子树…

系统优化工具 | PC Cleaner v9.7.0.3 绿色版

PC Cleaner是一款功能强大的电脑清理和优化工具&#xff0c;旨在通过清理系统垃圾文件、解除恶意软件和优化系统性能来提高计算机的运行效率。该软件提供了多种功能&#xff0c;可以帮助用户维护和提升计算机的整体表现。 PC Cleaner 支持 Windows 7 及以上操作系统&#xff0…

餐饮+KTV点歌一体化思路-—SAAS本地化及未来之窗行业应用跨平台架构

一、餐饮KTV点歌一体化 1. 多元化体验&#xff1a;为顾客提供了餐饮和娱乐的双重享受&#xff0c;满足了不同需求&#xff0c;增加了顾客的停留时间和消费可能性。 2. 增加消费机会&#xff1a;顾客在享受美食的同时可以唱歌娱乐&#xff0c;可能会增加酒水、小吃等额外消费。…

【我的 PWN 学习手札】Unlink Attack

目录 前言 一、Unlink介绍 二、保护和限制 &#xff08;1&#xff09;FD->bk P AND BK->fd P &#xff08;2&#xff09;chunksize(P) prev_size(next_chunk(P)) &#xff08;3&#xff09;largebin chunk 三、适用场景 四、利用与绕过 &#xff08;1&#…

Day 11-12:查找

目录 概念 方法 折半查找 前提 算法思路 分块查找 算法思路 哈希表 概念 构造哈希函数的方法 保留除数法 处理冲突的方法 开放地址法&#xff08;二次探查法&#xff09; 链地址法&#xff08;重要&#xff09; 哈希表的实现 结构体的创建 哈希表的创建 哈希…