最小生成树
给定一张边带权的无向图G=(V,E),n=|V|,m=|E|。由V中全部n个顶点和E中n-1条边构成的无向连通子图被称为G的一棵生成树。边的权值之和最小的生成树被称为无向图G的最小生成树(Minimum Spanning Tree, MST)。
给定一张无向图G=(V,E), n=|V|,m=|E|。从E中选出k<n-1条边构成G的一个生成森林。若再从剩余的m-k条边中选n-1-k条添加到生成森林中,使其成为G的生成树,并且选出的边的权值之和最小,则该生成树一定包含这m-k条边中连接生成森林的两个不连通节点的权值最小的边。
Prim算法
普利姆 (Prim) 算法是一种用于解决最小生成树问题的算法,又被称为加点法,适用于稠密图,其主要思路如下:
-
选择任意一个顶点作为起始点,将其加入最小生成树中。
-
从已选择的顶点集合中选取一个顶点,该顶点与未选择的顶点构成的边权重最小,并且该边的另一端顶点未被选择,将该顶点和边加入最小生成树中。
-
重复上一步骤 ,直到最小生成树包含了图中的所有顶点。
Kruskal算法
克鲁斯卡尔 (Kruskal) 算法是一种用于解决最小生成树问题的算法,又被称为加边法,适用于稀疏图,其主要思路如下:
-
建立并查集,每个点各自构成一个集合。
-
把所有边按照权值从小到大排列,依次扫描每条边(x,y,z)。
-
若x,y属于同一集合(连通),则忽略这条边,继续扫描下一条。
-
否则,合并x,y所在的集合,并把z累加到答案中。
-
所有边扫描完后,上一步处理的边就构成最小生成树。
例题及两种解法
点亮须弥【UUST】 - 蓝桥云课 (lanqiao.cn)
prim
#include<iostream>
#include<vector>
#include<queue>
#include<algorithm>
using namespace std;
const int INF = 0x3f3f3f3f;
struct side { int to, len; };
int prim(const vector<vector<side>>& edges,int start=0) {
const int n = edges.size();
vector<int> vis(n);
priority_queue<pair<int, int>> q;
q.push({ 0, 0 });
int ans = 0;
while (!q.empty()) {
int nowp = q.top().second;
int d = -q.top().first;
q.pop();
if (vis[nowp])continue;
ans += d;
vis[nowp] = true;
for (side s : edges[nowp]) {
if (vis[s.to])continue;
q.push({ -s.len,s.to });
}
}
for (bool f : vis)if (!f)return -1;
return ans;
}
int main() {
int n, m; cin >> n >> m;
vector<vector<side>> edges(n);
while (m--) {
int u, v, w;
cin >> u >> v >> w;
-- u, --v;
edges[u].push_back({ v,w });
edges[v].push_back({ u,w });
}
cout << prim(edges);
return 0;
}
kruskal
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<vector>
#include<functional>
#include<algorithm>
#include<numeric>
using namespace std;
struct side { int u, v, w; };
int main() {
int n, m;
scanf("%d%d", &n, &m);
vector<side> edges(m);
for (side& s : edges)scanf("%d%d%d", &s.u, &s.v, &s.w);
vector<int> fa(n + 1);
iota(fa.begin(), fa.end(), 0);
function<int(int)> find = [&](int x) {
if (x == fa[x])return x;
return fa[x]=find(fa[x]);//路径压缩
};
sort(edges.begin(), edges.end(), [](const side& s1, const side& s2) {
return s1.w < s2.w; });
int ans = 0;
for (side s : edges) {
int x = find(s.u);
int y = find(s.v);
if (x == y)continue;
fa[x] = y;
ans += s.w;
}
int flag = false;
for (int i = 1; i <= n; i++) {
if (i == fa[i] && !flag)flag = true;
else if (i == fa[i] && flag) {
cout << -1;
return 0;
}
}
cout << ans << endl;
return 0;
}