算法基本流程
这个算法基本流程还是很简单的,基本流程如下
-
输入:一个加权连通图,其中顶点集合为 ,边集合为 ;
-
初始化:,其中 为集合 中的任一节点(起始点), 为空;
-
重复下列操作,直到 :
- 在集合 中选取权值最小的边 ,其中 为集合 中的元素,而 不在 集合当中,并且 (如果存在有多条满足前述条件即具有相同权值的边,则可任意选取其中之一)
- 将 加入集合 中,将 边加入集合 中
-
输出:使用集合 和 来描述所得到的最小生成树。
总结: 这个算法就有一种一棵树从一个种子开始逐渐生长扩张,直到所有点包括到树的范围里的感觉。每次生长的时候 树都是选择里自己最近的点,然后把这个点吸收进自己的范围里。这样就能让最后的树的所有路径的长度的和最小。
算法流程图
在这个博客里能看到一个流程图,还挺清晰的: 博客传送门
算法代码
模板题传送门: 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;
}