c++最小生成树详讲

一、最小生成树(Minimum Spanning Tree)是什么?

1️⃣ 图论基础回顾

我们先明确研究对象:

  • 无向图
  • 连通
  • 带权图

设图 (G = (V, E))

  • (V):点集,(|V| = n)
  • (E):边集,(|E| = m)
  • 每条边有权值 (w)

2️⃣ 生成树(Spanning Tree)

定义

一棵 生成树 满足:

  1. 包含图中 所有顶点
  2. 是一棵
    • 连通
    • 无环
  3. 边数一定是:
    [
    |E| = n - 1
    ]

👉 一个连通图可能有 很多棵生成树


3️⃣ 最小生成树(MST)

定义

所有生成树 中,边权之和最小 的那一棵,称为:

最小生成树(Minimum Spanning Tree)

形式化

令生成树 (T\subseteq E),使得:

[
\sum_{e\in T} w(e)
]

取得最小值。


4️⃣ MST 的性质(非常重要)

性质 1:边数固定

  • 任意 MST 的边数 = (n-1)

性质 2:权值可能不唯一

  • MST 不一定唯一
  • 最小权值和一定唯一

性质 3:局部最优 → 全局最优

这是 MST 算法正确性的核心依据(贪心基础)。


二、MST 的核心理论基础

1️⃣ 割(Cut)定理(Kruskal / Prim 的理论支撑)

割的定义

将点集 (V) 划分为两个非空集合:

[
S \quad|\quad V-S
]

称为一个 割(Cut)


割边

  • 连接 (S) 和 (V-S) 的边

割定理(Cut Property)

对于任意一个割,权值最小的割边,一定属于某棵 MST

⚠️ 重点

  • 不一定属于所有 MST
  • 但一定属于 至少一棵 MST

2️⃣ 环(Cycle)定理

在一个环中,权值最大的边 不可能出现在 MST 中

这解释了:

  • Kruskal 为什么“遇到成环就跳过”
  • Prim 为什么只扩展最小边

三、Kruskal 算法(边优先)


1️⃣ 算法思想

一句话总结:

按边权从小到大排序,能加就加,不成环就要

具体步骤

  1. 将所有边按权值从小到大排序
  2. 初始化并查集(每个点自成一个集合)
  3. 枚举每条边 ((u,v))
    • find(u) != find(v)
      → 加入 MST
      → 合并集合
  4. 当选满 (n-1) 条边,结束

2️⃣ 为什么不会成环?

  • 并查集维护的是“连通块”
  • 若两点已经在同一集合
    • 说明路径已存在
    • 再连 → 必然成环

3️⃣ 时间复杂度分析

  • 排序:(O(m \log m))
  • 并查集:近似 (O(1))
  • 总体:
    [
    O(m \log m)
    ]

适合:

  • 稀疏图
  • (m) 较小

4️⃣ C++ 实现(竞赛标准写法)

#include<bits/stdc++.h>usingnamespace std;structEdge{int u, v;longlong w;}; vector<Edge> edges;int fa[200005];intfind(int x){if(fa[x]== x)return x;return fa[x]=find(fa[x]);}intmain(){int n, m; cin >> n >> m;for(int i =1; i <= n; i++) fa[i]= i;for(int i =0; i < m; i++){int u, v;longlong w; cin >> u >> v >> w; edges.push_back({u, v, w});}sort(edges.begin(), edges.end(),[](Edge a, Edge b){return a.w < b.w;});longlong ans =0;int cnt =0;for(auto e : edges){int fu =find(e.u);int fv =find(e.v);if(fu != fv){ fa[fu]= fv; ans += e.w; cnt++;if(cnt == n -1)break;}} cout << ans << endl;return0;}

5️⃣ 易错点总结(Kruskal)

❌ 忘记判断图是否连通
❌ 并查集未路径压缩
❌ 边数达到 n-1 还继续枚举
❌ 权值用 int 溢出


四、Prim 算法(点优先)


1️⃣ 算法思想

从一个点开始,每次选一条连接“已选点集”和“未选点集”的最小边

与 Dijkstra 非常像,但本质不同。


2️⃣ 算法步骤(堆优化版)

  1. 随机选一个起点
  2. 将起点加入 MST
  3. 用优先队列维护:
    • 所有“已访问点 → 未访问点”的边
  4. 每次取最小边
  5. 若终点未访问 → 加入 MST
  6. 重复直到选满 (n-1) 条边

3️⃣ 与 Dijkstra 的区别(常考)

对比项PrimDijkstra
目标最小生成树最短路径
dist 含义到 MST 的最小边到源点最短距离
累加方式不累加路径路径累加

4️⃣ 时间复杂度

  • 堆优化:
    [
    O(m \log n)
    ]

适合:

  • 稠密图
  • 邻接表结构

5️⃣ C++ 实现(堆优化)

#include<bits/stdc++.h>usingnamespace std;structEdge{int to;longlong w;}; vector<Edge> g[200005];bool vis[200005];intmain(){int n, m; cin >> n >> m;for(int i =0; i < m; i++){int u, v;longlong w; cin >> u >> v >> w; g[u].push_back({v, w}); g[v].push_back({u, w});} priority_queue<pair<longlong,int>, vector<pair<longlong,int>>, greater<pair<longlong,int>>> pq;longlong ans =0; pq.push({0,1});// 从 1 号点开始while(!pq.empty()){auto[w, u]= pq.top(); pq.pop();if(vis[u])continue; vis[u]=true; ans += w;for(auto e : g[u]){if(!vis[e.to]){ pq.push({e.w, e.to});}}} cout << ans << endl;return0;}

6️⃣ Prim 常见坑

⚠️ 图必须连通
⚠️ 初始点权值为 0
⚠️ 不要重复加边权
⚠️ vis 判断必须在弹出后


五、Kruskal vs Prim 对比总结

项目KruskalPrim
思路边贪心点贪心
数据结构并查集优先队列
适合稀疏图稠密图
是否依赖起点
实现难度⭐⭐⭐⭐⭐

六、MST 进阶与拓展(OI 必看)


1️⃣ 判断 MST 是否唯一

方法:

  • 若存在 相同权值边
  • 且在同一割中可以替换
    不唯一

竞赛中常结合 次小生成树


2️⃣ 次小生成树

权值严格大于 MST,且最小

做法:

  • 枚举非 MST 边
  • 替换路径中最大边

需要:

  • LCA + 最大边维护

3️⃣ Kruskal 重构树(提高级)

用于:

  • MST 上的路径最大/最小查询
  • 多用于 离线问题

4️⃣ 动态 MST(了解)

  • 边动态增删
  • 通常使用:
    • LCT(Link-Cut Tree)

七、典型例题(洛谷)

  • P3366 【模板】最小生成树
  • P2872 工程规划
  • P4180 严格次小生成树

Read more

Git 一个本地仓库同时推送到两个远程仓库(详细教程)

Git 一个本地仓库同时推送到两个远程仓库(详细教程)

一. 引言 我们在实际工作中经常会涉及项目备份问题,尽管公司都有自己的仓库,但是呢很多情况下我们都还需要另外一个仓库。 一方面公司仓库通常需要公司内网或者链接VPN,有时候临时要用可能并不方便。 另一方面可能自己也希望备份一份,或者是一个仓库的成员不够用。 本篇博客会通过完整的实战步骤,来介绍如何实现Git双远程仓库同步。 二. 查看当前的远程仓库 执行命令: git remote -v 通常来讲输入如下: origin https://***@bitbucket.org/louis_private_project/ignoremodules.git (fetch) origin https://***@bitbucket.org/louis_private_project/ignoremodules.git (push) 这就表示现在只绑定了一个远程仓库。 它的含义是: * 拉取代码(fetch/pull) * 推送代码(push) Git 把拉取和推送地址分开显示了,默认情况下,pull 和

By Ne0inhk
OpenClaw六大开源替代方案深度解析,从极简原型到生产级巨兽的全面选型指南

OpenClaw六大开源替代方案深度解析,从极简原型到生产级巨兽的全面选型指南

在AI智能体领域,2025年无疑是属于“Claw”系列的一年。这一年11月,Peter Steinberger上传了一个名为OpenClaw的原型代码,谁也没想到,这个项目会在短短84天内收获20万颗GitHub Star,成为史上增长最快的软件项目。OpenClaw的爆火,就像一颗投入湖面的石子,瞬间激起了整个AI智能体生态的涟漪,一系列轻量级、针对性更强的开源替代方案纷纷涌现,它们各自在代码规模、安全性能、部署难度等维度上做出优化,满足不同开发者的多样化需求。 对于开发者而言,面对市面上五花八门的Claw类项目,如何根据自身的技术背景、部署环境和实际需求,选择最适合自己的方案,成为了一个亟待解决的问题。有的开发者追求极简代码,想快速理解AI智能体的核心逻辑;有的则看重安全性能,需要能直接用于生产环境的可靠方案;还有的专注于边缘计算,希望智能体能在资源受限的硬件上顺畅运行。为了帮大家理清思路,本文将对六大主流开源Claws项目进行深度对比,从项目概述、技术栈、性能指标到适用人群,再到部署要求和核心特性的差异,全方位解析每个项目的优势与不足,最后给出针对性的选型建议,希望能为开发者们

By Ne0inhk

New API 详解:新一代开源大模型统一网关与 AI 资产管理系统(深度 6000 字指南)

New API 详解:新一代开源大模型统一网关与 AI 资产管理系统(深度 6000 字指南) * 开篇:为什么我们需要一个“大模型统一网关”? * 一、项目背景与发展历程 * 二、核心特性详解(为什么 New API 比竞品强) * 1. 统一接口 + 多格式转换(最强兼容性) * 2. 智能路由与高可用 * 3. 精细计费与支付闭环(个人/企业必备) * 4. 现代化管理后台 * 5. 多语言 & 多租户 * 6. 扩展集成 * 7. 安全与可观测性 * 三、支持的模型与渠道(30+ 服务商,100+ 模型) * 四、部署安装完整教程(10 分钟上手)

By Ne0inhk
2025年ChipCamp芯片营地十大开源RISC-V

2025年ChipCamp芯片营地十大开源RISC-V

1、XiangShan ----官方地址:https://github.com/OpenXiangShan/XiangShan ----中科院计算所/中国科学院大学的开源RISC-V项目,面向RISC-V的最前沿技术和场景,使用Chisel语言,打造的高性能超标量乱序RISC-V处理器,经过雁栖湖,南湖,昆明湖多代版本的演进,代码和文档全部开源。 ----作为专业的超大规模RISC-V开源项目,在github上获得6.8K个star。 ----被ChipCamp收录在:https://gitcode.com/ChipCamp/XiangShan。 2、RISCV-BOOM ----官方地址:https://github.com/riscv-boom/riscv-boom ----在Github上有2K个星,其BOOM-v1.0代码在ChipCamp芯片营地行了通读。 ----被ChipCamp收录在:https://gitcode.com/ChipCamp/BOOM-v1.0。 3、e203_hbirdv2 ----官方地址(国际):https:

By Ne0inhk