最小生成树 | P1546 [USACO3.1]最短网络 Agri-Net
题意
给定一个 N×N 的矩阵,分别表示 1∼N 号点到其它点的距离,求连接这 N 个点的最短路径。
分析
将邻接矩阵转换为一个 N 个点的完全图,题目所求即为该图的最小生成树。
1.最小生成树
对一张图 G=(V,E),若存在另一张图 H=(V′,E′) 满足 V′⊆V 且 E′⊆E,则称 H 是 G 的 子图 (subgraph),记作 H⊆G。
若 H⊆G 满足 V′=V,则称 H 为 G 的 生成子图/支撑子图 (spanning subgraph)。
特别地,若 G 的子图 H 是一棵树,则称 H 为 G 的 生成树 (spanning tree)。
我们定义无向连通图的 最小生成树 (Minimum Spanning Tree,MST) 为边权和最小的生成树。
2.Kruscal 算法
Kruskal 算法是一种常见的最小生成树算法。该算法的基本思想是按边权从小到大加边,本质上是贪心算法。
具体来说,维护一个森林,每次查询两个结点是否在同一棵树中,若不在,则连接两棵树。我们可以通过并查集实现这个算法。
代码
#include<bits/stdc++.h>
using namespace std;
const int N = 1e4 + 5;
int n, fa[N], u, v, w, m;
struct edge{ // 维护边的结构体
int u, v, w;
bool operator < (const edge &x){
return w < x.w;
}
}e[N];
inline int xfind(int x){ // 并查集的查询操作
return fa[x] == x ? x : (fa[x] = xfind(fa[x]));
}
inline int kruscal(){ // 求最小生成树
int cnt = 0, ans = 0;
sort(e + 1, e + m + 1); // 对边按权值从小到大排序
for(int i = 1; i <= n; i++) fa[i] = i; // 初始化并查集数组
for(int i = 1; i <= m; i++){
u = xfind(e[i].u), v = xfind(e[i].v);
if(u == v) continue; // 查询 u 与 v 是否在同一集合
ans += e[i].w, fa[u] = v; // 若 u 与 v 不在同一集合,合并
if(++cnt == n - 1) return ans; // 最小生成树只有 n - 1 条边
}
return -1;
}
int main(){
ios::sync_with_stdio(false);
cin >> n;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
cin >> w;
if(i < j) e[++m] = {i, j, w}; // 将邻接矩阵转换
}
}
cout << kruscal();
return 0;
}
望穿寂夜晨曦至,雄鹰展翅图九天。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】