Prim
算法,用来求解「加权连通图」的最小生成树,适用「稠密图」
最小生成树:
即连通图的「最小权重」生成树
Prim
属于贪心算法,维持一个顶点集合
基于边集的Kruskal
算法同样属于贪心算法,适用于「稀疏图」
朴素Prim算法
n 代表顶点数量,m 代表边的数量
时间复杂度为 O(n2)
算法流程
图中所有顶点集合设为 V ,维持一个顶点集合 Vnew=S 表示已经加入生成树的顶点
记 V′=V−Vnew 为点集 S 的补集
1.初始化距离数组 dist[]=INF ,表示每个顶点到生成树(即集合 S )的最短距离
2.选取一个顶点加入 S
3.迭代 n-1 次
在每次迭代中,如果当前 V′ 距离 S 最短为 INF 的话,意味着:
当前图不存在最小生成树,或者说当前图有多个连通分量,或者说存在「生成森林」
可直接返回
Tips
1.
Prim
算法和Dijkstra
算法的区别在于
Prim 算法中的 dist[] 数组表示的是,每个顶点到生成树(即集合 S )的最短距离
Dijkstra 中的 dist[] 表示的是,每个顶点到源点的最短距离
2.
最小生成树,可以用于求解:不同城市之间铺设铁路,求总铁路长度最短的方案等问题
3.
注意,最小生成树,根本上还是一颗树,满足树的全部性质
树是图的子集,是有向无环图
代码实现
核心代码
解析见注释
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| int prim() { Arrays.fill(dist, INF); st[1] = true; for (int j = 1; j <= n; ++j) { dist[j] = Math.min(dist[j], g[1][j]); }
int res = 0; for (int i = 1; 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 (dist[t] == INF) return INF;
res += dist[t]; st[t] = true;
for (int j = 1; j <= n; ++j) { dist[j] = Math.min(dist[j], g[t][j]); } } return res; }
|
java版完整代码
acwing 858. Prim算法求最小生成树
给定一个 n 个点 m 条边的无向图,图中可能存在重边和自环,边权可能为负数。
求最小生成树的树边权重之和,如果最小生成树不存在则输出 impossible
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
| import java.util.*; import java.io.*;
class Main { static BufferedWriter log = new BufferedWriter(new OutputStreamWriter(System.out)); static BufferedReader cin = new BufferedReader(new InputStreamReader(System.in)); static final int N = 510; static final int INF = 0x3f3f3f3f; static int[][] g = new int[N][N]; static int[] dist = new int[N]; static boolean[] st = new boolean[N]; static int n, m;
static int prim() { Arrays.fill(dist, INF); st[1] = true; for (int j = 1; j <= n; ++j) { dist[j] = Math.min(dist[j], g[1][j]); }
int res = 0; for (int i = 1; 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 (dist[t] == INF) return INF;
res += dist[t]; st[t] = true;
for (int j = 1; j <= n; ++j) { dist[j] = Math.min(dist[j], g[t][j]); } } return res; }
public static void main(String[] args) throws Exception { String[] str = cin.readLine().split(" "); n = Integer.parseInt(str[0]); m = Integer.parseInt(str[1]); int a, b, w; for (int i = 1; i <= n; ++i) { Arrays.fill(g[i], INF); } while (m-- > 0) { str = cin.readLine().split(" "); a = Integer.parseInt(str[0]); b = Integer.parseInt(str[1]); w = Integer.parseInt(str[2]); g[a][b] = Math.min(g[a][b], w); g[b][a] = g[a][b]; }
int t = prim(); if (t == INF) log.write("impossible\n"); else log.write(String.valueOf(t));
log.flush(); log.close(); cin.close(); } }
|
堆优化
算法整体和 Dijkstra 很相似,同样可以进行堆优化
原理不再分析
堆优化版本的时间复杂度为 O(mlogn) 的,适用于稀疏图,但是稀疏图的话,写 Kruskal 更方便,因此堆优化版本的 Prim 算法不常用
核心代码👇🏻
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| int prim() { Arrays.fill(dist, INF);
int res = 0; PriorityQueue<int[]> heap = new PriorityQueue<>((a, b) -> { return a[1] - b[1]; }); heap.offer(new int[]{1, INF}); int cnt = 0; while (!heap.isEmpty()) { int[] cur = heap.poll(); int ver = cur[0], distance = cur[1]; if (st[ver]) continue; if (ver != 1) res += distance; cnt++; st[ver] = true; for (int i = 1; i <= n; ++i) { if (g[ver][i] == INF || i == ver) continue; if (dist[i] > g[ver][i]) { dist[i] = g[ver][i]; heap.offer(new int[]{i, dist[i]}); } } } return cnt == n ? res : INF; }
|
java版完整代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
| import java.util.*; import java.io.*;
class Main { static BufferedWriter log = new BufferedWriter(new OutputStreamWriter(System.out)); static BufferedReader cin = new BufferedReader(new InputStreamReader(System.in)); static final int N = 510; static final int INF = 0x3f3f3f3f; static int[][] g = new int[N][N]; static int[] dist = new int[N]; static boolean[] st = new boolean[N]; static int n, m; static int prim() { Arrays.fill(dist, INF); int res = 0; PriorityQueue<int[]> heap = new PriorityQueue<>((a,b)->{return a[1] - b[1];}); heap.offer(new int[]{1, INF}); int cnt = 0; while (!heap.isEmpty()) { int[] cur = heap.poll(); int ver = cur[0], distance = cur[1]; if (st[ver]) continue; if (ver != 1) res += distance; cnt++; st[ver] = true; for (int i = 1; i <= n; ++i) { if (g[ver][i] == INF || i == ver) continue; if (dist[i] > g[ver][i]) { dist[i] = g[ver][i]; heap.offer(new int[]{i, dist[i]}); } } } return cnt == n ? res : INF; }
public static void main(String[] args) throws Exception { String[] str = cin.readLine().split(" "); n = Integer.parseInt(str[0]); m = Integer.parseInt(str[1]); int a, b, w; for (int i = 1; i <= n; ++i) { Arrays.fill(g[i], INF); } while (m-- > 0) { str = cin.readLine().split(" "); a = Integer.parseInt(str[0]); b = Integer.parseInt(str[1]); w = Integer.parseInt(str[2]); g[a][b] = Math.min(g[a][b], w); g[b][a] = g[a][b]; }
int t = prim(); if (t == INF) log.write("impossible\n"); else log.write(String.valueOf(t));
log.flush(); log.close(); cin.close(); } }
|
📔博文图谱
提及本博文的链接