最小生成树 | P1546 [USACO3.1]最短网络 Agri-Net

题目传送门

题意

给定一个 N×N 的矩阵,分别表示 1N 号点到其它点的距离,求连接这 N 个点的最短路径。

分析

将邻接矩阵转换为一个 N 个点的完全图,题目所求即为该图的最小生成树。

1.最小生成树

对一张图 G=(V,E),若存在另一张图 H=(V,E) 满足 VVEE,则称 H 是 G 的 子图 (subgraph),记作 HG

HG 满足 V=V,则称 HG生成子图/支撑子图 (spanning subgraph)

特别地,若 G 的子图 H 是一棵树,则称 HG生成树 (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;
}
posted @   心灵震荡  阅读(17)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 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】
点击右上角即可分享
微信分享提示