2025年Prim算法

Prim算法Prim 算法 Prim 普利姆 算法是求解最小生成树 的经典算法 什么是最小生成树 即给定一个无向图 在这个无向图中选择一些边能够把图中的所有结点都连接起来 并且所有边的长度之和是最短的 下图中红色的边就形成了一棵最小生成树 红色的边将所有的点都连接起来了 所有红色边的权重之和最小

大家好,我是讯享网,很高兴认识大家。

Prim算法

Prim(普利姆)算法是求解最小生成树的经典算法

什么是最小生成树

即给定一个无向图,在这个无向图中选择一些边能够把图中的所有结点都连接起来,并且所有边的长度之和是最短的

下图中红色的边就形成了一棵最小生成树,红色的边将所有的点都连接起来了,所有红色边的权重之和最小

在这里插入图片描述
讯享网

Prim算法基本思想

我们维护一个集合,最开始集合为空,所有边的权重初始为无穷大(实际上我们并不用设置无穷大,用一个相对较大的数就可以了,代表两点之间不可达)。我们每次都选取距离集合最近的一个点(即距离集合权重最小的点),选取之后就将该点加入集合,并且用该点 更新 与该点相连的 还没加入集合点的权重。

  1. 初始化
    在这里插入图片描述
  2. 选取节点 1 1 1 加入集合,并用该点更新权重(注意:第一步的节点可以随便选),此时集合为 { 1 } \{1\} { 1}

在这里插入图片描述

  1. 选取距离集合最近的一个点加入集合,并更新权重,第二次选择节点 2 2 2,集合为 { 1 , 2 } \{1,2\} { 1,2}

在这里插入图片描述

  1. 选取距离集合最近的一个点加入集合,并更新权重,第二次选择节点 3 3 3,集合为 { 1 , 2 , 3 } \{1,2,3\} { 1,2,3}

在这里插入图片描述

  1. 选取距离集合最近的一个点加入集合,并更新权重,第二次选择节点 7 7 7,集合为 { 1 , 2 , 3 , 7 } \{1,2,3,7\} { 1,2,3,7}

在这里插入图片描述

就这样一直迭代下去,直到将所有的点都加入到集合中。在迭代的过程中我们可以记录最小生成数的权值和,也可以将最小生成树中的边打印出来。

2.代码实现

输入:

7 11 1 2 18 1 7 18 1 6 19 2 7 20 2 3 8 3 4 20 4 7 15 4 6 16 4 5 9 5 6 3 6 7 15 

讯享网

输出:

讯享网1 ---> 2 2 ---> 3 1 ---> 7 7 ---> 4 4 ---> 5 5 ---> 6 dist 18 8 8 9 3 3 15 距离 -> 71 

实现代码:

#include<bits/stdc++.h> using namespace std; const int N = 510,INF = 0x3f3f3f3f; int g[N][N],dist[N];// g是邻接矩阵 用于存储边 g[a][b] = w 代表 a 到 b 的距离为w //dist[i] 代表与i点相连的最短一条边的距离  int n,m; //要求给定n个点 和 m条边 bool st[N]; //st记录某一个点是否加入集合 st[i] = true 代表i点已经加入集合 反之则没有 int prim(){ 
    //最开始初始化为无穷大 memset(dist,0x3f,sizeof dist); int ans = 0; //由于我们需要将 给定的 n个点都加入集合,所以需要循环n次 for(int i = 0;i < n;i++){ 
    //从 尚未加入集合 的点里面选取一个距离集合最短的点,如果集合为空则选择第一个点 int t = -1; for(int j = 1;j <= n;j++){ 
    if(!st[j] && (t==-1 || dist[t] > dist[j])) t = j; } //从集合中找出 距离 当前点 t 最近的点 //pre -> t 的边就是新加入最小生成树的边 if(i > 0){ 
    int ans = INF; int pre = -1; for(int j = 1;j <= n;j++){ 
    if(st[j]){ 
    if(ans > g[j][t]){ 
    ans = g[j][t]; pre = j; } } } printf("%d ---> %d\n",pre,t); } //如果存在有些点不可达,就无法形成最小生成树,直接返回 //当 i = 0 时,此时集合是为空的,第一个要加入到集合中的点是1,此时还没加入到集合 //并且也没更新距离,所以需要跳过 if(i > 0 && dist[t] == INF) return INF; //将 点t 加入到集合中 st[t] = true; //当i > 0,比如 i = 1,也就是第二轮循环,此时集合中有2个点,才能确定一条边 if(i > 0) ans += dist[t];//记录最小生成树每条边的距离 //用点t 更新距离 for(int j = 1;j <= n;j++){ 
    dist[j] = min(dist[j],g[t][j]); } } return ans; } int main(){ 
    cin>>n>>m; memset(g,0x3f,sizeof g); while(m--){ 
    int a,b,w; scanf("%d%d%d",&a,&b,&w); //因为是无向图 即 a 可以到达 b,b 也可以到达 a //可能会存在重边的情况,我们只需要记录最小值 g[a][b] = g[b][a] = min(g[a][b],w); } int t = prim(); printf("dist "); for(int i = 1;i <= n;i++) printf("%d ",dist[i]); puts(""); if(t == INF) puts("不存在最小生成树!!!"); else printf("距离 -> %d\n",t); return 0; } 

3.例题

题目链接

Prim算法求最小生成树

题目描述

给定一个 n n n 个点 m m m 条边的无向图,图中可能存在重边和自环,边权可能为负数

求最小生成树的树边权重之和,如果最小生成树不存在则输出 impossible

给定一张边带权的无向图 G = ( V , E ) G=(V,E) G=(V,E),其中 V V V 表示图中点的集合, E E E 表示图中边的集合, n = ∣ V ∣ n=|V| n=V m = ∣ E ∣ m=|E| m=E

V V V 中的全部 n n n 个顶点和 E E E n − 1 n−1 n1 条边构成的无向连通子图被称为 G G G 的一棵生成树,其中边的权值之和最小的生成树被称为无向图 G G G最小生成树

输入格式

第一行包含两个整数 n n n m m m

接下来 m m m 行,每行包含三个整数 u , v , w u,v,w u,v,w,表示节点 u u u 和 节点 v v v 之间存在一条权值为 w w w 的边。

输出格式

共一行,若存在最小生成树,则输出一个整数,表示最小生成树的树边权重之和,如果最小生成树不存在则输出 impossible

数据范围
  • ≤ n ≤ 500 \leq n \leq500 n500
  • 1 ≤ m ≤ 1 0 5 1 \leq m \leq 10^5 1m105
  • 图中涉及边的边权的绝对值均 不超过 10000 10000 10000

输入样例:

讯享网4 5 1 2 1 1 3 2 1 4 3 2 3 2 3 4 4 

输出样例:

6 

时间复杂度: O ( n 2 ) O(n^2) O(n2)

C++代码:

讯享网#include<bits/stdc++.h> using namespace std; const int N = 510,INF = 0x3f3f3f3f; int g[N][N],dist[N]; int n,m; bool st[N]; int prim(){ 
    memset(dist,0x3f,sizeof dist); int ans = 0; for(int i = 0;i < n;i++){ 
    int t = -1; for(int j = 1;j <= n;j++){ 
    if(!st[j] && (t==-1 || dist[t] > dist[j])) t = j; } if(i && dist[t] == INF) return INF; st[t] = true; if(i) ans += dist[t]; for(int j = 1;j <= n;j++) dist[j] = min(dist[j],g[t][j]); } return ans; } int main(){ 
    cin>>n>>m; memset(g,0x3f,sizeof g); while(m--){ 
    int a,b,w; scanf("%d%d%d",&a,&b,&w); g[a][b] = g[b][a] = min(g[a][b],w); } int t = prim(); if(t == INF) puts("impossible"); else printf("%d\n",t); return 0; } 
小讯
上一篇 2025-02-19 20:14
下一篇 2025-02-14 19:55

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/44623.html