最小生成树小结(MST问题) Kruskal 算法 Prim算法 POJ 1258 HDOJ 1233

2023-10-17 09:32

本文主要是介绍最小生成树小结(MST问题) Kruskal 算法 Prim算法 POJ 1258 HDOJ 1233,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

      • Kruskal 算法
      • Prim算法
      • 总结

Kruskal 算法

算法步骤:
1、初始化时所有结点属于孤立的集合

2、按照边权递增顺序 遍历所有的边,若遍历到的边连接的两个顶点分属于不同的集合(该边即为连通这两个集合的边中权值最小的那条),则确定该边为最小生成树上的一条边,并将这两个顶点分属的集合合并。

3、遍历完所有的边后,若是原图上所有结点属于同一个集合,则被选取的边和原图中的所有结点构成最小生成树;否则原图不连通,最小生成树不存在。

如步骤所示,在用Kruskal算法求解最小生成树问题时涉及到大量的集合操作,我们可以使用并查集来实现这些操作。

记忆要点:按照边权递增顺序排序 、并查集判断是否属于同一个集合

模板代码:

int tree[maxn];
void init(int x) {//并查集初始化for (int i = 1; i <= x; i++) { tree[i] = -1; }
}
int findroot(int a) {//寻找根结点if (tree[a] == -1) return a;else {int tmp = findroot(tree[a]);tree[a] = tmp;//路径压缩return tmp;}
}
struct Edge {//存放边结构int from, to, cost;
}edge[maxm];
bool cmp(Edge a, Edge b) {//按照边权递增顺序排序return a.cost < b.cost;
}
int main() {...sort(edge + 1, edge + 1 + cnt, cmp);int ans = 0;for (int i = 1; i <= cnt; i++) {int u = findroot(edge[i].from);int v = findroot(edge[i].to);if (u != v) {tree[u] = v;//合并ans += edge[i].cost;}}cout << ans << endl;}
}

下面给出的例题都不存在得不到最小生成树的情况,所以最后我们并没有对所有结点是否属于同一个集合进行判断,若可能出现不存在最小生成树的情况,则该步不能省略。

POJ 1258 Agri-Net 题目链接 最小生成树入门题目

Sample Input
4
0 4 9 21
4 0 8 17
9 8 0 16
21 17 16 0
Sample Output
28
题目大意:
给你N*N矩阵,表示N个村庄之间的距离。现在要把N个村庄全都连接起来,求连接的最短距离。

AC代码:

#include<iostream>
#include<stdio.h>
#include<algorithm>
using namespace std;
#define maxn 101
#define INF 0x3fffffff
int tree[maxn];
void init(int x) {for (int i = 1; i <= x; i++) { tree[i] = -1; }
}
int findroot(int a) {if (tree[a] == -1) return a;else {int tmp = findroot(tree[a]);tree[a] = tmp;//路径压缩return tmp;}
}
struct Edge {int from, to, cost;
}edge[5000];
bool cmp(Edge a, Edge b) {return a.cost < b.cost;
}
int main() {int n;while (cin >> n) {init(n);int w, cnt = 0;//cnt代表边的条数for (int i = 1; i <= n; i++) {for (int j = 1; j <= n; j++) {cin >> w;if (j > i) {edge[++cnt].from = i;edge[cnt].to = j;edge[cnt].cost = w;}}}sort(edge + 1, edge + 1 + cnt, cmp);int ans = 0;for (int i = 1; i <= cnt; i++) {int u = findroot(edge[i].from);int v = findroot(edge[i].to);if (u != v) {tree[u] = v;//合并ans += edge[i].cost;}}cout << ans << endl;}
}

HDOJ 1233 还是通畅工程 题目链接

Problem Description
某省调查乡村交通状况,得到的统计表中列出了任意两村庄间的距离。省政府“畅通工程”的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可),并要求铺设的公路总长度为最小。请计算最小的公路总长度。
Input
测试输入包含若干测试用例。每个测试用例的第1行给出村庄数目N ( < 100 );随后的N(N-1)/2行对应村庄间的距离,每行给出一对正整数,分别是两个村庄的编号,以及此两村庄间的距离。为简单起见,村庄从1到N编号。
当N为0时,输入结束,该用例不被处理。
Output
对每个测试用例,在1行里输出最小的公路总长度。
Sample Input
3
1 2 1
1 3 2
2 3 4
4
1 2 1
1 3 4
1 4 1
2 3 3
2 4 2
3 4 5
0
Sample Output
3
5

题目大意:
在给定的道路中选取一些,使所有的城市直接或间接连通且使道路的总长度
最小,该例即为典型的最小生成树问题。我们将城市抽象成图上的结点,将道路
抽象成连接点的边,其长度即为边的权值。经过这样的抽象,我们求得该图的最
小生成树,其上所有的边权和即为所求。

AC代码:

#include<iostream>
#include<stdio.h>
#include<algorithm>
using namespace std;
#define N 101
int Tree[N];int findRoot(int a) {if (Tree[a] == -1) {return a;}else {int tmp = findRoot(Tree[a]);Tree[a] = tmp;//路径压缩return tmp;}
}
struct Edge {int a, b;int cost;
};
Edge edge[5000];
bool cmp(Edge a, Edge b) {return a.cost < b.cost;
}int main() {int n;while (scanf("%d",&n)!=EOF) {if (n == 0) { break; }int a, b, mycost;for (int i = 1; i <= n; i++) {Tree[i] = -1;}int time = n*(n - 1) / 2;for (int i = 1; i <= time; i++) {scanf("%d%d%d", &edge[i].a, &edge[i].b, &edge[i].cost);}sort(edge + 1, edge + 1 + time, cmp);int ans = 0;for (int i = 1; i <= time; i++) {int a = findRoot(edge[i].a);int b = findRoot(edge[i].b);if (a != b) {//两树合并Tree[a] = b;ans += edge[i].cost;}}printf("%d\n", ans);}
}

Prim算法

与Dijkstra算法十分相似,以图上的某一顶点为出发点,逐次选择到最小生成树顶点集距离最短的顶点为最小生成树的顶点,并加入到该顶点集,直到包含所有的顶点。

步骤:

1.选择一出发点,加入集合A。

2.遍历与集合A中的点相邻的边,在不构成回路的情况下找到最短的边。

3.将步骤2得到的边的目标点加入集合A。

4.重复2,3直到所有结点都加入到集合A中,如果算法结束后还有结点不在集合A中,则不存在最小生成树。

核心代码:

int map[maxn][maxn];
int d[maxn];//用于存放连通某一顶点所需要的代价
bool mark[maxn];
int n;//顶点个数
int prim(int s) {for (int i = 1; i <= n; i++) {d[i] = INF;mark[i] = false;}int newnode = s;mark[newnode] = true;d[newnode] = 0;int ans = 0;//返回值for (int i = 1; i < n; i++) {//循环n-1次for (int j = 1; j <= n; j++) {if (mark[j] || map[newnode][j] == INF) continue;if (d[j] == INF || d[j] > map[newnode][j]) {d[j] = map[newnode][j];}}int tmp = INF;for (int j = 1; j <= n; j++) {//寻找最小值加入集合if (mark[j] || d[j] == INF) continue;if (d[j] < tmp) {tmp = d[j];newnode = j;}}if (tmp == INF) return -1;//不存在最小生成树 提前剪枝mark[newnode] = true;ans += d[newnode];}return ans;
}

POJ 1258 Agri-Net AC代码:

#include<iostream>
#include<stdio.h>
#include<algorithm>
using namespace std;
#define INF 0x3fffffff
#define maxn 101
int map[maxn][maxn];
int d[maxn];//用于存放连通某一顶点所需要的代价
bool mark[maxn];
int n;//顶点个数
int prim(int s) {for (int i = 1; i <= n; i++) {d[i] = INF;mark[i] = false;}int newnode = s;mark[newnode] = true;d[newnode] = 0;//将一号结点作为选择的第一个结点int ans = 0;//返回值for (int i = 1; i < n; i++) {//循环n-1次for (int j = 1; j <= n; j++) {if (mark[j] || map[newnode][j] == INF) continue;if (d[j] == INF || d[j] > map[newnode][j]) {d[j] = map[newnode][j];}}int tmp = INF;for (int j = 1; j <= n; j++) {//寻找最小值加入集合if (mark[j] || d[j] == INF) continue;if (d[j] < tmp) {tmp = d[j];newnode = j;}}if (tmp == INF) return -1;//不存在最小生成树 提前剪枝mark[newnode] = true;ans += d[newnode];}return ans;
}int main() {while (scanf("%d", &n) != EOF) {for (int i = 1; i <= n; i++) {for (int j = 1; j <= n; j++) {scanf("%d", &map[i][j]);}}cout << prim(n) << endl;}return 0;
}

HDOJ 1233 还是通畅工程 AC代码:

#include<iostream>
#include<stdio.h>
#include<algorithm>
using namespace std;
#define INF 0x3fffffff
#define maxn 101
int map[maxn][maxn];
int d[maxn];//用于存放连通某一顶点所需要的代价
bool mark[maxn];
int n;//顶点个数
void init(int x) {for (int i = 1; i <= x; i++) {for (int j = 1; j <= x; j++) {map[i][j] = INF;}}
}
int prim(int s) {for (int i = 1; i <= n; i++) {d[i] = INF;mark[i] = false;}int newnode = s;mark[newnode] = true;d[newnode] = 0;//将一号结点作为选择的第一个结点int ans = 0;//返回值for (int i = 1; i < n; i++) {//循环n-1次for (int j = 1; j <= n; j++) {if (mark[j] || map[newnode][j] == INF) continue;if (d[j] == INF || d[j] > map[newnode][j]) {d[j] = map[newnode][j];}}int tmp = INF;for (int j = 1; j <= n; j++) {//寻找最小值加入集合if (mark[j] || d[j] == INF) continue;if (d[j] < tmp) {tmp = d[j];newnode = j;}}if (tmp == INF) return -1;//不存在最小生成树 提前剪枝mark[newnode] = true;ans += d[newnode];}return ans;
}int main() {while (scanf("%d", &n) != EOF) {if (n == 0)break;init(n);int time = n*(n - 1) / 2;int u, v, w;for (int i = 1; i <= time; i++) {scanf("%d%d%d", &u, &v, &w);map[u][v] = min(map[u][v], w);map[v][u] = map[u][v];}cout << prim(n) << endl;}return 0;
}

总结

Kruskal的算法复杂度为O(eloge),与网中的边数有关,适合于稀疏图;而Prim算法的时间复杂度为O(n^2),与网中的边数无关,适合于稠密图。

这篇关于最小生成树小结(MST问题) Kruskal 算法 Prim算法 POJ 1258 HDOJ 1233的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/224491

相关文章

Java内存泄漏问题的排查、优化与最佳实践

《Java内存泄漏问题的排查、优化与最佳实践》在Java开发中,内存泄漏是一个常见且令人头疼的问题,内存泄漏指的是程序在运行过程中,已经不再使用的对象没有被及时释放,从而导致内存占用不断增加,最终... 目录引言1. 什么是内存泄漏?常见的内存泄漏情况2. 如何排查 Java 中的内存泄漏?2.1 使用 J

numpy求解线性代数相关问题

《numpy求解线性代数相关问题》本文主要介绍了numpy求解线性代数相关问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 在numpy中有numpy.array类型和numpy.mat类型,前者是数组类型,后者是矩阵类型。数组

Python使用qrcode库实现生成二维码的操作指南

《Python使用qrcode库实现生成二维码的操作指南》二维码是一种广泛使用的二维条码,因其高效的数据存储能力和易于扫描的特点,广泛应用于支付、身份验证、营销推广等领域,Pythonqrcode库是... 目录一、安装 python qrcode 库二、基本使用方法1. 生成简单二维码2. 生成带 Log

解决systemctl reload nginx重启Nginx服务报错:Job for nginx.service invalid问题

《解决systemctlreloadnginx重启Nginx服务报错:Jobfornginx.serviceinvalid问题》文章描述了通过`systemctlstatusnginx.se... 目录systemctl reload nginx重启Nginx服务报错:Job for nginx.javas

Redis缓存问题与缓存更新机制详解

《Redis缓存问题与缓存更新机制详解》本文主要介绍了缓存问题及其解决方案,包括缓存穿透、缓存击穿、缓存雪崩等问题的成因以及相应的预防和解决方法,同时,还详细探讨了缓存更新机制,包括不同情况下的缓存更... 目录一、缓存问题1.1 缓存穿透1.1.1 问题来源1.1.2 解决方案1.2 缓存击穿1.2.1

redis-cli命令行工具的使用小结

《redis-cli命令行工具的使用小结》redis-cli是Redis的命令行客户端,支持多种参数用于连接、操作和管理Redis数据库,本文给大家介绍redis-cli命令行工具的使用小结,感兴趣的... 目录基本连接参数基本连接方式连接远程服务器带密码连接操作与格式参数-r参数重复执行命令-i参数指定命

Python视频处理库VidGear使用小结

《Python视频处理库VidGear使用小结》VidGear是一个高性能的Python视频处理库,本文主要介绍了Python视频处理库VidGear使用小结,文中通过示例代码介绍的非常详细,对大家的... 目录一、VidGear的安装二、VidGear的主要功能三、VidGear的使用示例四、VidGea

Python中的随机森林算法与实战

《Python中的随机森林算法与实战》本文详细介绍了随机森林算法,包括其原理、实现步骤、分类和回归案例,并讨论了其优点和缺点,通过面向对象编程实现了一个简单的随机森林模型,并应用于鸢尾花分类和波士顿房... 目录1、随机森林算法概述2、随机森林的原理3、实现步骤4、分类案例:使用随机森林预测鸢尾花品种4.1

vue解决子组件样式覆盖问题scoped deep

《vue解决子组件样式覆盖问题scopeddeep》文章主要介绍了在Vue项目中处理全局样式和局部样式的方法,包括使用scoped属性和深度选择器(/deep/)来覆盖子组件的样式,作者建议所有组件... 目录前言scoped分析deep分析使用总结所有组件必须加scoped父组件覆盖子组件使用deep前言

解决Cron定时任务中Pytest脚本无法发送邮件的问题

《解决Cron定时任务中Pytest脚本无法发送邮件的问题》文章探讨解决在Cron定时任务中运行Pytest脚本时邮件发送失败的问题,先优化环境变量,再检查Pytest邮件配置,接着配置文件确保SMT... 目录引言1. 环境变量优化:确保Cron任务可以正确执行解决方案:1.1. 创建一个脚本1.2. 修