最小生成树
这篇博客是关于图论中的最小生成树,在这里我们先简单介绍最小生成树的概念:一个图中,选取总代价最小的边使得所有点都连通。由此得到的结果必然是一棵树,同时需要注意一个图的最小生成树不具有唯一性。下面介绍求最小生成树的一种方法:普里姆算法求最小生成树。
普里姆算法正确性可以使用反证法进行证明,这里不进行展开。
普里姆算法的核心为以下步骤:
(1)使用一个d数组用于存储各个节点到树的距离,初始化d数组为无穷大。使用一个book数组用于区分某个节点是树节点还是非树节点,book数组初始化为false。首先从任意一个顶点开始构造树,按照习惯从第1号节点开始构造树,将d[1] = 0,book[1] = true。
(2)更新与1号节点相连的非树节点到树的距离,也就是更新d数组,更新条件为:如果i节点到树的距离比d[i]中存储的距离小,那么更新d[i]为更小的值。
(3)遍历更新后的d数组,找到非树节点中距离树最小的节点,将这个节点加入树中。
(4)重复步骤1、2、3,直到生成树中有n个节点为止。
下面为实现代码:
#include <iostream> #include <cstdio> using namespace std; const int MAXN = 5001; const int MAXM = 20001; struct Line { int p; int next; int v; }; struct Node { int dis; bool book; }; Line line[MAXM * 2]; Node node[MAXN]; int h[MAXN]; int n, m, x, y, v, now, ans; void add(int x, int y, int v, int lID) { line[lID].p = y; line[lID].v = v; line[lID].next = h[x]; h[x] = lID; } int find() { int k = 0; for(int i = 1; i <= n; i++) { if(!node[i].book) { if(k == 0 || node[i].dis < node[k].dis) { k = i; } } } return k; } void update(int now) { int k = h[now]; int p; while(k) { p = line[k].p; if(!node[p].book) { if(node[p].dis > line[k].v) { node[p].dis = line[k].v; } } k = line[k].next; } } int main() { scanf("%d %d", &n, &m); for(int i = 1; i <= m; i++) { scanf("%d %d %d", &x, &y, &v); add(x, y, v, 2 * i - 1); add(y, x, v, 2 * i); } for(int i = 1; i <= n; i++) { node[i].book = false; node[i].dis = (1 << 31) - 1; } node[1].book = true; node[1].dis = 0; ans = 0; update(1); for(int i = 1; i < n; i++) { now = find(); node[now].book = true; ans += node[now].dis; update(now); } printf("%d", ans); return 0; }
圆满结束。