news 2026/4/15 13:09:32

【模板】最小生成树(洛谷P3366)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【模板】最小生成树(洛谷P3366)

题目描述

如题,给出一个无向图,求出最小生成树,如果该图不连通,则输出orz

输入格式

第一行包含两个整数 N,M,表示该图共有 N 个结点和 M 条无向边。

接下来 M 行每行包含三个整数 Xi​,Yi​,Zi​,表示有一条长度为 Zi​ 的无向边连接结点 Xi​,Yi​。

输出格式

如果该图连通,则输出一个整数表示最小生成树的各边的长度之和。如果该图不连通则输出orz

输入输出样例

输入 #1复制运行

4 5 1 2 2 1 3 2 1 4 3 2 3 4 3 4 3

输出 #1复制运行

7

说明/提示

数据规模:

对于 20% 的数据,N≤5,M≤20。

对于 40% 的数据,N≤50,M≤2500。

对于 70% 的数据,N≤500,M≤104。

对于 100% 的数据:1≤N≤5000,1≤M≤2×105,1≤Zi​≤104,1≤Xi​,Yi​≤N。

样例解释:

所以最小生成树的总边权为 2+2+3=7。

1. 问题引入

在图论中,最小生成树是一个非常经典的问题:

在一个拥有N个顶点和M条边的带权无向图中,如何找到一棵树,使得它包含所有N个顶点,且所有边的权值之和最小?

此外,还有一个常见变种:如果图本身就是断开的(不连通),我们该如何判断?

本文将基于 Kruskal 算法,结合并查集,给出一种通解。

2. 核心算法:Kruskal (克鲁斯卡尔)

Kruskal 算法的核心思想是“贪心”

算法步骤:

  1. 排序:将所有的边按照权值(w)从小到大排序。我们希望尽可能的选权值小的边。

  2. 遍历:按顺序枚举每一条边(u, v)。

  3. 判断

    • 如果u和v已经在同一个集合里(连通),说明加上这条边会形成环,跳过

    • 如果u和v不在同一个集合,说明这条边连接了两个独立的连通分量,选中它。

    • 将u和v通过并查集合并。

  4. 终止:当我们选够了N-1条边时,最小生成树构建完成。

关键点:连通性判断

如果遍历完所有边,选中的边数仍然小于N-1,说明图是不连通的(有孤岛),此时无法生成 MST,按照题目要求输出 "orz" 。

3. 核心代码解析

3.1 结构体运算符重载

为了方便排序,我们直接在结构体内部重载<运算符。这样调用sort时就不需要写额外的cmp函数,代码封装性更好。

struct edge{ int x,y,w; //重载运算符 让边集数组按照边权从小到大排序 friend bool operator <(edge a,edge b){ return a.w<b.w; } }e[200010];//边集数组 edge mst[5010];//记录最小生成树每条边

3.2 并查集的路径压缩

这是 Kruskal 能高效运行的保证。find函数在递归查找祖先的过程中,直接将沿途节点的父指针指向根节点,将树高度压扁。

int find(int x) { if(fa[x]==x) return x; return fa[x]=find(fa[x]); // 路径压缩,下次查就是 O(1) }

4. 完整代码

//kruskal版 //首先明确什么是不连通,不连通代表图有多个连通分量 //即最后生成最小生成树后所用的边小于n-1(n是节点数) #include <iostream> #include <algorithm>//sort函数头文件 using namespace std; int fa[5010];//并查集 记录每个节点的父/祖先节点(集合中的老大) int n,m;//n个结点 m条无向边 int ans=0;//ans是mst(最小生成树)的边数 记录连了多少条边 //并查集查询+路径压缩 int find(int x){ //如果已经是根结点(集合老大)就返回 if(fa[x]==x) return x; //否则就递归找老大,并把老大给到沿途所有节点 return fa[x]=find(fa[x]); } void uni(int u,int v){ int fau=find(u);//找u老大 int fav=find(v);//找v老大 //如果老大相同就说明已经在一个集合就不需要操作 if(fau!=fav){//如果不同,就让u的老大变成v的老大的老大 fa[fav]=fau; } } struct edge{ int x,y,w; //重载运算符 让边集数组按照边权从小到大排序 friend bool operator <(edge a,edge b){ return a.w<b.w; } }e[200010];//边集数组 edge mst[5010];//记录最小生成树每条边 int main(){ ios::sync_with_stdio(false); cin.tie(0); cin>>n>>m;//n个结点 m条无向边 //初始化每个节点的老大为自己(每个点自己就是一个连通分量) for(int i=1;i<=n;i++) fa[i]=i; //创建边集数组 for(int i=1;i<=m;i++){ cin>>e[i].x>>e[i].y>>e[i].w; } //边集数组按照边权从小到大排序 sort(e+1,e+m+1); int ma=0;//记录最小生成树各边的长度之和 //取每一条边,看这条边的两端是不是同一个连通分量 for(int i=1;i<=m;i++){ int u=e[i].x; int v=e[i].y; //如果u v不连通 就把它两连起来 并把边权加进max //如果已经连通就跳过 if(find(u)!=find(v)){ ans++;//每连一条边 ans+1 //存最小生成树的边(这道题用不到) mst[ans].x=u; mst[ans].y=v; mst[ans].w=e[i].w; uni(u,v);//这条边的两端需要变成一个连通分量(集合) ma+=e[i].w; //最小生成树最多只能有n-1条边 if(ans==n-1) break; } } //如果 连接的边数==节点数-1,说明是连通图 if(ans==n-1) cout<<ma; //不等于 说明有多个连通分量 else cout<<"orz"; return 0; }

5. 总结与易错点

  1. 关于 "orz":Kruskal算法不仅能求最短路径和,还能天然地判断图的连通性。如果循环结束后ans<n-1,说明即使把所有能连的边都连上,图还是断开的。

  2. 数据范围fa数组开N大小,e数组开M大小。不要搞混。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/13 20:41:35

登上Nature子刊的捷径:LPJ模型+NPP模拟+气候响应全流程

随着全球气候变化的日益严峻&#xff0c;理解和预测植被生产力的变化变得尤为重要。此次主要目的是深入探讨植被净初级生产力&#xff08;NPP&#xff09;的模拟、驱动力分析及其气候变化响应&#xff0c;利用LPJ模型为研究工具&#xff0c;帮助学员掌握从GPP到NPP、NEP/NEE等关…

作者头像 李华
网站建设 2026/4/15 18:23:25

R语言的贝叶斯网络模型的实践

在现代的生态、环境以及地学研究中&#xff0c;变量和变量间的因果关系推断占据了非常重要的地位。在实践中&#xff0c;变量间的因果关系研究往往求助于昂贵的实验&#xff0c;但所得结果又经常与天然环境中的实际因果联系相差甚远。统计学方法是研究天然环境中变量间关系的好…

作者头像 李华
网站建设 2026/4/10 9:50:51

【必藏】构建高并发AI系统:从量化剪枝到边缘部署的完整实践指南

本文详细介绍了大规模AI系统的设计与优化技术&#xff0c;包括模型量化、剪枝等推理优化方法&#xff0c;不同平台部署策略&#xff0c;实时应用的延迟与吞吐量平衡&#xff0c;边缘AI部署&#xff0c;系统瓶颈诊断与性能监控&#xff0c;以及AI系统的CI/CD流水线和调试工具&am…

作者头像 李华
网站建设 2026/4/13 21:27:21

Spring Boot核心注解详解:@ResponseBody深度解析与实战

在Spring MVC/Spring Boot的开发体系中&#xff0c;前后端分离已是主流架构模式&#xff0c;而数据交互的核心离不开各类注解的支撑。其中&#xff0c;ResponseBody作为处理HTTP响应的关键注解&#xff0c;是后端返回数据给前端的“桥梁”。本文将从核心作用、工作原理、实战示…

作者头像 李华
网站建设 2026/4/15 11:21:29

python基于flask框架的高校实验室管理系统

目录高校实验室管理系统摘要开发技术路线相关技术介绍核心代码参考示例结论源码lw获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01;高校实验室管理系统摘要 高校实验室管理系统基于Flask框架开发&#xff0c;旨在通过信息化手段解决传统实验室管…

作者头像 李华