数据结构与算法学习笔记----Prim算法
数据结构与算法学习笔记----Prim算法
@@ author: 明月清了个风
@@ first publish time: 2024.12.21
Prim算法
Prim算法是一种基于贪心策略的最小生成树求解算法,和Dijkstra很相似。
最小生成树
最小生成树(Minimum Spanning Tree, MST)是图论中的经典问题,对于一个无向带权图,它指的是一个包含图中所有顶点的生成树,其中边的权值和最小,它的性质主要有下面三个
- 最小生成树包含所有的顶点
- 最小生成树中没有环
- 边的权值和最小。
基本思想
- 从图中任意一个顶点出发,逐步扩展生成树
- 在已经生成的树的基础上,选择在树外距离与树连通并距离最近的节点加入树中,并用该点的出边更新其他点到树的距离
- 重复该过程,直到生成树包含了所有的节点。
通过与Dijkstra算法对比可以发现,两个算法步骤的唯一区别在于上述思想中的第二步,对于Dijkstra来说,我们选择的是在已经确定最短距离的点集外的点中距离源点距离最近的点,而Prim算法选择是已经包含在树中之外的点中距离树中任意节点距离最近的点。
两者的区别在于更新其他点的距离的时候,对于Dijkstra来说,更新距离是
for(int j = 1; j <= n; j ++)
dist[j] = min(dist[j], dist[t] + g[t][j]);
对于Prim算法来说
for(int j = 1; j <= n; j ++)
dist[j] = min(dist[j], g[t][j]);
时间复杂度
Floyd算法的时间复杂度为 O ( n 3 ) O(n^3) O(n3), n n n表示节点数。
Acwing 858. Prim算法求最小生成树
[原题链接](858. Prim算法求最小生成树 - AcWing题库)
给定一个 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 n−1条边构成的无向连通子图被称为 G G G的一棵生成树,其中边的权值之和最小的生成树被称为无向图 G G G的最小生成树。
输入格式
第一行包含三个整数 n n n, m m m。
接下来 m m m行,每行包含三个整数 x x x、 y y y、 z z z,表示存在一条从点 x x x到点 y y y的有向边,边长为 z z z。
输出格式
共一行,若存在最小生成树,则输出一个整数,表示最小生成树的树边权重之和,如果最小生成树不存在则输出impossible
.
数据范围
1 ≤ n ≤ 500 1 \leq n \leq 500 1≤n≤500,
1 ≤ m ≤ 1 0 5 1 \leq m \leq 10^5 1≤m≤105,
图中涉及边长绝对值均不超过 10000 10000 10000.
代码
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 510, inf = 0x3f3f3f3f;
int n, m;
int dist[N];
int g[N][N];
bool st[N];
int prim()
{
int res = 0;
memset(dist, 0x3f, sizeof dist);
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;
}
st[t] = true;
if(i && dist[t] == inf) return inf;
if(i) res += dist[t]; // 这里有个注意点是要先更新答案res再去更新边权,因为可能有负权自环,这个dist[t]可能会把自己变小。
for(int j = 1; j <= n; j ++)
dist[j] = min(dist[j], g[t][j]);
}
return res;
}
int main()
{
cin >> n >> m;
memset(g, 0x3f, sizeof g);
for(int i = 0; i < m; i ++)
{
int a, b, c;
cin >> a >> b >> c;
g[a][b] = g[b][a] = min(g[a][b], c);
}
int t = prim();
if(t == inf) puts("impossible");
else cout << t << endl;
return 0;
}