算法基本流程
这个算法基本流程还是很简单的,基本流程如下
-
输入:一个加权连通图,其中顶点集合为 ,边集合为 ;
-
初始化:,其中 为集合 中的任一节点(起始点), 为空;
-
重复下列操作,直到 :
- 在集合 中选取权值最小的边 ,其中 为集合 中的元素,而 不在 集合当中,并且 (如果存在有多条满足前述条件即具有相同权值的边,则可任意选取其中之一)
- 将 加入集合 中,将 边加入集合 中
-
输出:使用集合 和 来描述所得到的最小生成树。
总结: 这个算法就有一种一棵树从一个种子开始逐渐生长扩张,直到所有点包括到树的范围里的感觉。每次生长的时候 树都是选择里自己最近的点,然后把这个点吸收进自己的范围里。这样就能让最后的树的所有路径的长度的和最小。
算法流程图
在这个博客里能看到一个流程图,还挺清晰的: 博客传送门
算法代码
模板题传送门: https://www.luogu.com.cn/problem/P3366
#include <iostream>#include <queue>#include <stdio.h>#include <string.h>#include <vector>
using std::cin;using std::cout;using std::priority_queue;using std::vector;
const int N = 5e3 + 5;
int in_v_cnt = 0;bool is_in_v[N];
struct Target { int target_node; int cost;
bool operator>(const Target &other) const { return cost > other.cost; }};
vector<Target> adj[N];
void add(int a, int b, int c) { adj[a].push_back({b, c}); adj[b].push_back({a, c});}
priority_queue<Target, vector<Target>, std::greater<Target>> global_heap;
int main() { memset(is_in_v, 0, sizeof(is_in_v)); int n, m; cin >> n >> m;
int a, b, c, v_added; bool has_new_v; for (int i = 0; i < m; i++) { cin >> a >> b >> c; if (i == 0) { v_added = a; is_in_v[a] = true; in_v_cnt++; has_new_v = true; } add(a, b, c); }
auto result = 0; while (in_v_cnt < n && has_new_v) { // 将所有刚添加到树上的节点的邻边添加到堆里 for (auto &target : adj[v_added]) { global_heap.push(target); }
auto tmp_target = global_heap.top(); while (is_in_v[tmp_target.target_node] && !global_heap.empty()) { global_heap.pop(); tmp_target = global_heap.top(); }
has_new_v = false; // 找到了一个还没有添加进 v 的节点,并且还是最近的 if (!is_in_v[tmp_target.target_node]) { v_added = tmp_target.target_node; is_in_v[v_added] = true; in_v_cnt++; result += tmp_target.cost;
has_new_v = true; } }
if (in_v_cnt < n) { cout << "orz"; } else { cout << result; }
return 0;}