Acwing 最小生成树

最小生成树

最小生成树:由n个节点,和n-1条边构成的无向图被称为G的一棵生成树,在G的所有生成树中,边的权值之和最小的生成树,被称为G的最小生成树。(换句话说就是用最小的代价把n个点都连起来)

  • Prim 算法(普利姆)
    • 朴素版Prim(时间复杂度 O ( n 2 ) O(n^2) O(n2),适用于稠密图;
    • 堆优化版Prim(时间复杂度 O ( m l o g n ) O(m log n) O(mlogn),适用于稀疏图),基本不用;
  • Kruskal算法,适用于稀疏图,时间复杂度 O ( m l o g m ) O(m log m) O(mlogm).$

如果是稠密图,通常选用朴素版Prim算法,因为其思路比较简洁,代码比较短;如果是稀疏图,通常选用Kruskal算法,因为其思路比Prim简单清晰。堆优化版的Prim通常不怎么用。

1.Prim

朴素Prim
与朴素dijkstra思想几乎一样,只不过Prim算法的距离指得是点到最小生成树的集合的距离,而Dijkstra算法的距离是点到起点的距离。适合用于稠密图
Prim 算法过程:

  • 初始化 dist 数组,表示各点到最小生成树的距离。开始时设为无穷大(INF)。
  • 使用贪心策略,每次选取距离集合最近的点 t,并将其加入最小生成树。
  • 若该点距离 INF 且不是第一个节点,表示图不连通,直接返回 INF。
  • 更新其余未加入集合的点与最小生成树的最短距离。
  • 最终返回最小生成树的总权值,若图不连通则输出 impossible。

实现思路:

  • 初始化各点到集合的距离INF;
  • n次循环,每次找到集合外且距离集合最近的点t,需要先判断除第一个点外找到的距离最近的点t距离是不是INF;
    • 若是则不存在最小生成树了,结束;否则还可能存在,继续操作,用该点t来更新其它点到集合的距离(这里就是和Dijkstra最主要的区别),然后将该点t加入集合;
  • 关于集合到距离最近的点:实际上就是不在集合的点与集合内的点的各个距离的最小值,每次加入新的点都会尝试更新一遍(dist[j] = min(dist[j],g[t][j]).在Dijkstra中是dist[j] - min(dits[j],dist[t] + g[t][j]));
    在这里插入图片描述

板子:

const int N = 510, INF = 0x3f3f3f3f; // 定义最大节点数 N 和无穷大 INF
int g[N][N], dist[N]; // g[i][j] 表示从节点 i 到节点 j 的边权,dist[i] 表示当前生成树到节点 i 的最短距离
bool st[N]; // st[i] 表示节点 i 是否已经加入到最小生成树中int n, m; // n 表示节点数量,m 表示边的数量// 返回最小生成树的权值之和。如果图不连通,返回 INF
int prim() {memset(dist, 0x3f, sizeof dist); // 初始化所有节点的最短距离为 INF(即无穷大)int res = 0; // 最终的最小生成树权值之和// Prim 算法的核心,循环 n 次,每次找到一个未加入集合的最近节点for (int i = 0; i < n; i++) {int t = -1; // 用于记录当前找到的距离最小的节点// 找到距离当前生成树最近的未加入集合的节点for (int j = 1; j <= n; j++) {if (!st[j] && (t == -1 || dist[t] > dist[j])) t = j;}// 如果当前是第一个节点或者找到的节点距离集合为 INF,说明图不连通if (i && dist[t] == INF) return INF; // 无法构成最小生成树if (i) res += dist[t]; // 将该节点的边权值加入最终结果中// 更新其他未加入集合的节点到生成树的最短距离for (int j = 1; j <= n; j++) {dist[j] = min(dist[j], g[t][j]); // 使用该节点 t 更新其他节点的最短距离}st[t] = true; // 标记该节点已经加入生成树}return res; // 返回最小生成树的总权值
}

Acwing 858.Prim 算法求最小生成树
在这里插入图片描述
注意:本题干未设起点所以要迭代n次,并且图中可能存在负的自环,因此计算最小生成树的总距离要在更新各点到集合距离之前。且该图为无向图,含重边,则构建边需要注意。

具体实现代码(详解版):

#include <iostream>
#include <cstring>
#include <algorithm>using namespace std;const int N = 510, INF = 0x3f3f3f3f; // 定义最大节点数 N 和无穷大 INF
int g[N][N], dist[N]; // g[i][j] 表示从节点 i 到节点 j 的边权,dist[i] 表示当前生成树到节点 i 的最短距离
bool st[N]; // st[i] 表示节点 i 是否已经加入到最小生成树中int n, m; // n 表示节点数量,m 表示边的数量// 返回最小生成树的权值之和。如果图不连通,返回 INF
int prim() {memset(dist, 0x3f, sizeof dist); // 初始化所有节点的最短距离为 INF(即无穷大)int res = 0; // 最终的最小生成树权值之和// Prim 算法的核心,循环 n 次,每次找到一个未加入集合的最近节点for (int i = 0; i < n; i++) {int t = -1; // 用于记录当前找到的距离最小的节点// 找到距离当前生成树最近的未加入集合的节点for (int j = 1; j <= n; j++) {if (!st[j] && (t == -1 || dist[t] > dist[j])) t = j;}// 如果当前是第一个节点或者找到的节点距离集合为 INF,说明图不连通if (i && dist[t] == INF) return INF; // 无法构成最小生成树if (i) res += dist[t]; // 将该节点的边权值加入最终结果中// 更新其他未加入集合的节点到生成树的最短距离for (int j = 1; j <= n; j++) {dist[j] = min(dist[j], g[t][j]); // 使用该节点 t 更新其他节点的最短距离}st[t] = true; // 标记该节点已经加入生成树}return res; // 返回最小生成树的总权值
}int main() {cin >> n >> m; // 输入节点数 n 和边数 mmemset(g, 0x3f, sizeof g); // 初始化邻接矩阵,所有边权值设为无穷大(表示节点间无边)// 输入每条边的信息,构建邻接矩阵while (m--) {int a, b, c;cin >> a >> b >> c;g[a][b] = g[b][a] = min(g[a][b], c); // 无向图,所以 g[a][b] 和 g[b][a] 相同}int t = prim(); // 调用 Prim 算法计算最小生成树的权值// 输出结果。如果返回的是 INF,说明图不连通,输出 "impossible"if (t == INF) puts("impossible");else cout << t << endl; // 否则输出最小生成树的权值return 0;
}

堆优化Prim:思路和堆优化的Dijkstra思路基本一样,且基本不用,对于稀疏图,不如用Kruskal,这里略过

2. Kruskal

适用于稀疏图,时间复杂度 O ( m l o g m ) O(m log m) O(mlogm).Kruskal 算法是求解无向连通图的 最小生成树(MST)的经典算法。它的核心思想是通过贪心策略,按权值从小到大逐步选择边,确保不会产生环,直到选出 n-1 条边为止。整个过程涉及使用 并查集 来判断是否形成环

  • 先将所有的边按照权重,从小到大排序;
  • 从小到大枚举每条边(a,b,w)若a,b不连通,则将这条边加入集合中(将a点和b点连接起来)实质上是一个并查集的应用(两点之间加边,看两点是否存在一个连通块)
  • 无需用邻接表、邻接矩阵存储图,直接使用结构体,表示边及其权值

在这里插入图片描述
板子:

const int N = 200010, INF = 0x3f3f3f3f;
int p[N]; // 并查集的父节点数组
int n, m; // n 表示节点数,m 表示边数// 边的结构体,表示一条边及其权值
struct Edge {int a, b, w; // a, b 为连接的两个节点,w 为权值// 重载小于运算符,用于按边权值升序排序bool operator<(const Edge &W) const {return w < W.w;}
} edges[N]; // 存储所有边// 并查集:寻找节点 x 所在集合的根节点
int find(int x) {if (p[x] != x) p[x] = find(p[x]); // 路径压缩,找到根节点并直接连接return p[x];
}// Kruskal 算法求最小生成树
int kruskal() {sort(edges, edges + m); // 按照边的权值升序排序for (int i = 1; i <= n; i++) p[i] = i; // 初始化并查集,每个节点的父节点是自己int res = 0, cnt = 0; // res 存储最小生成树的权值之和,cnt 记录最小生成树的边数// 遍历所有边for (int i = 0; i < m; i++) {int a = edges[i].a, b = edges[i].b, w = edges[i].w;// 找到两个节点的根节点a = find(a), b = find(b);if (a != b) { // 如果两点不在同一个集合中,说明它们不连通p[a] = b; // 将两点所在的集合合并res += w; // 累加这条边的权值cnt++; // 增加计数}}// 如果使用的边数不足 n-1,则说明图不连通,无法构成最小生成树if (cnt < n - 1) return INF;else return res; // 返回最小生成树的权值
}

具体实现代码(详解版):

#include <iostream>
#include <cstring>
#include <algorithm>using namespace std;const int N = 200010, INF = 0x3f3f3f3f;
int p[N]; // 并查集的父节点数组
int n, m; // n 表示节点数,m 表示边数// 边的结构体,表示一条边及其权值
struct Edge {int a, b, w; // a, b 为连接的两个节点,w 为权值// 重载小于运算符,用于按边权值升序排序bool operator<(const Edge &W) const {return w < W.w;}
} edges[N]; // 存储所有边// 并查集:寻找节点 x 所在集合的根节点
int find(int x) {if (p[x] != x) p[x] = find(p[x]); // 路径压缩,找到根节点并直接连接return p[x];
}// Kruskal 算法求最小生成树
int kruskal() {sort(edges, edges + m); // 按照边的权值升序排序for (int i = 1; i <= n; i++) p[i] = i; // 初始化并查集,每个节点的父节点是自己int res = 0, cnt = 0; // res 存储最小生成树的权值之和,cnt 记录最小生成树的边数// 遍历所有边for (int i = 0; i < m; i++) {int a = edges[i].a, b = edges[i].b, w = edges[i].w;// 找到两个节点的根节点a = find(a), b = find(b);if (a != b) { // 如果两点不在同一个集合中,说明它们不连通p[a] = b; // 将两点所在的集合合并res += w; // 累加这条边的权值cnt++; // 增加计数}}// 如果使用的边数不足 n-1,则说明图不连通,无法构成最小生成树if (cnt < n - 1) return INF;else return res; // 返回最小生成树的权值
}int main() {cin >> n >> m; // 输入节点数和边数for (int i = 0; i < m; i++) {int a, b, w;cin >> a >> b >> w; // 输入每条边的两个端点 a, b 和边的权值 wedges[i] = {a, b, w}; // 存储边的信息}int t = kruskal(); // 调用 Kruskal 算法if (t == INF) puts("impossible"); // 如果返回值为 INF,说明图不连通else cout << t << endl; // 否则输出最小生成树的权值return 0;
}

Kruskal 算法的核心思想:

  • 贪心策略:每次选择权值最小的边,且不形成环;
  • 并查集:用于快速检查两个节点是否已经连通,以及合并不同的连通集合。

3.对比总结

在这里插入图片描述

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

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

相关文章

shell 脚本练习

一、初识shell 文件描述符与重定向 0&#xff1a;标准输入 1&#xff1a;标准正确输出 2&#xff1a;标准错误输出 1>a.txt 与 >a.txt 一样&#xff0c;不写默认前面为1&#xff0c;省略&#xff0c;把标准输出重定向到a.txt中 2>a.txt 将错误输出重定向到a.txt中 &a…

在 CentOS 安装 Python3.7 (没有弯路)

下载Python源码包 wget https://www.python.org/ftp/python/3.7.12/Python-3.7.12.tgz安装前准备 安装依赖组件 yum -y install wget zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gcc make libffi-devel xz-devel解压安装 解…

【Redis】渐进式遍历 数据库管理命令 RESP协议

目录 渐进式遍历 scan 数据库管理命令 切换数据库 获取当前数据库key的个数 删除当前数据库所有的key 删除所有数据库中所有的key RESP协议 渐进式遍历 Redis使用scan命令进行渐进式遍历键&#xff0c;进而解决直接使用keys获取键时可能出现的阻塞问题&#xff08;因为…

ctf.show---->r3

做题笔记。 下载 查壳。 64ida打开。 先运行一下&#xff1a; &#xff1f; 不是说4位数值么。。。 得&#xff0c;看ida。 __isoc99_sscanf(dest, "%x", &v5); 懂了 dest指向的是v5 ,那么dest怎么来的&#xff1a; 往上走&#xff1a; 通过strncpy得到 但是问题…

Arthas redefine(加载外部的.class文件,redefine到JVM里 )

文章目录 二、命令列表2.2 class/classloader相关命令2.2.3 redefine&#xff08;加载外部的.class文件&#xff0c;redefine到JVM里 &#xff09;举例1&#xff1a;加载新的代码&#xff0c;jad/mc 命令使用举例2&#xff1a;上传 .class 文件到服务器的技巧 二、命令列表 2.…

WEB服务器——Tomcat

服务器是可以使用java完成编写&#xff0c;是可以接受页面发送的请求和响应数据给前端浏览器的&#xff0c;而在开发中真正用到的Web服务器&#xff0c;我们不会自己写的&#xff0c;都是使用目前比较流行的web服务器。 如&#xff1a;Tomcat 1. 简介 Tomcat 是一个开源的轻量…

汽车总线之---- LIN总线

Introduction LIN总线的简介&#xff0c;对于传统的这种点对点的连接方式&#xff0c;我们可以看到ECU相关的传感器和执行器是直接连接到ECU的&#xff0c;当传感器和执行器的数量较少时&#xff0c;这样的连接方式是能满足要求的&#xff0c;但是随着汽车电控功能数量的不断增…

Vue下载静态文件

1、需求&#xff1a;将静态文件放在本地&#xff0c;让用户进行下载。 2、文件位置&#xff1a; ① 原生js&#xff1a;直接将文件放在某个目录或者根目录下 ② Vue&#xff1a;将文件放在根目录的public文件夹下面 3、代码示例&#xff1a; const url "/模板.xlsx"…

Halcon实用系列1-识别二维条码

在做项目时&#xff0c;之前使用的是某康的智能读码器&#xff0c;综合考虑成本&#xff0c;可通过相机拍照来读取图片的二维码&#xff0c;我这边用Halcon来实现。 Halcon代码如下&#xff1a; *创建模型 create_data_code_2d_model(Data Matrix ECC 200, [], [], DataCodeH…

linux蓝屏重启解决方法汇总

前言 linux系统蓝屏&#xff08;Blue Screen Of Death&#xff09;是Linux系统用户遇到最严重的故障&#xff0c;任何新手都无法直接解决它。在遇到蓝屏时&#xff0c;最好的解决方案是联系Linux专业供应商或Linux专业支持工程师&#xff0c;因为他们有系统的协议和经验来解决…

dockerfile部署springboot项目(构建镜像:ebuy-docker:v1.0)

文章目录 1、docker部署Mysql2、dockerfile构建镜像1.1、在idea中导入课件中的项目资料\day01\ebuy-docker1.2、修改项目application.yml数据库连接参数1.3、启动项目访问测试&#xff1a;http://localhost:8081/1.4、执行mvn package命令进行项目打包1.5、虚拟机中新建目录/op…

SpringBoot(Java)实现MQTT连接(本地Mosquitto)通讯调试

1.工作及使用背景 工作中需要跟收集各种硬件或传感器数据用于Web展示及统计计算分析&#xff0c;如电表、流量计、泵、控制器等物联网设备。 目前的思路及解决策略是使用力控或者杰控等组态软件实现数据的转储&#xff08;也会涉及收费问题&#xff09;&#xff0c;通过组态软件…

NDI多画面系统(Multiview Pro)

NDI多画面系统(Multiview Pro)是千视以Multiview Player为基础打造的一款全新的多画面监看切换系统,支持自定义多画面/多窗口显示,单窗口可监看20路视频流,可实现多窗口、多屏幕预监+切换,且兼容NDI High Bandwidth和NDI HX/NDI HX3。同时Multiview Pro基于WebRTC技术,可…

用 Git Absorb 轻松管理 commit,告别频繁 fixup,效率提升 10 倍!

你是不是经常在使用 Git 的时候被频繁的 commit --fixup 弄得头疼?尤其是在修复代码时,一个小改动就得新建一个 commit,搞得整个 commit 历史乱七八糟,不仅影响工作效率,后来要查找问题时也变得更复杂。如果你对这个问题深有感触,那么这篇文章就是为你写的。 今天我想…

JVM 类加载机制2

扩展类加载器&#xff08;Extension ClassLoader&#xff09;&#xff0c;该类加载器是由 ExtClassLoader&#xff08;sun.misc.Launcher$ExtClassLoader&#xff09;实现&#xff0c;负责将 <JRE_HOME>/lib/ext 或者被 java.ext.dir 系统变量所指定路径中的所有类库加载…

open-resty 服务安装kafka插件

从github下载 作者&#xff1a;程序那点事儿 日期&#xff1a;2023/11/16 22:01 lua-resty-kafka 插件安装 下载代码后直接解压 mkdir -p /usr/local/openresty/modules/ #创建一个目录&#xff0c;存放lua插件cd /usr/local/openresty/modules/ #进入目录rz -y #上传lua插件…

SOMEIP_ETS_136: SD_Option_Length_shorter_GT_0_as_specified_for_type

测试目的&#xff1a; 验证DUT能够处理一个UDP选项长度小于其类型所指定长度的SubscribeEventgroup消息&#xff0c;并以SubscribeEventgroupNAck作为响应或完全忽略该请求。 描述 本测试用例旨在确保DUT遵循SOME/IP协议&#xff0c;当接收到一个UDP选项长度小于其类型所指定…

WPS在表格中填写材料时,内容过多导致表格不换页,其余内容无法正常显示 以及 内容过多,导致表格换页——解决方法

一、现象 1&#xff0c;内容过多导致表格不换页&#xff0c;其余内容无法正常显示 2&#xff0c;内容过多&#xff0c;导致表格换页 二、解决方法 在表格内右击&#xff0c;选择表格属性 在菜单栏选择行&#xff0c;勾选允许跨页断行&#xff0c;点击确定即可 1&#xff0…

windows安装Redis以后配置远程访问

修改配置文件&#xff1a; 第一个地方&#xff1a; 第二个地方&#xff1a; 启动服务&#xff1a; redis-server .\redis.windows.conf 可能需要重启计算机 经过实测&#xff0c;这个配置文件也得改&#xff1a; 如果不想重启计算机&#xff0c;可以执行下面的命令重启…

代码随想录冲冲冲 Day56 图论Part8

117. 软件构建 这道题是使用拓扑排序的方法 看多个任务有优先级的情况下 怎么排序 对应到这道题就是文件排序 首先要记录每一个点的入度&#xff0c;当一个点的入度为0时&#xff0c;就说明这个点是顶点 然后记录每一个点向那些点相连 之后建立一个queue 寻找一个入度为0的…