例题
-
以下代码均可A此题
kruskal
-
Kruskal算法通过并差集维护,从到小枚举每条边,如果两端点不在一个集合,将两端点所在集合合并,并将边权累加到答案中
-
时间复杂度为(O(m log m))
-
code
#include <cstdio> #include <algorithm> using namespace std; const int N = 5005, M = 2e5+5; struct side { int x, y, d; }e[M]; bool operator < (side a, side b) { return a.d < b.d; } int n, m, f[N], ans, cnt; int found(int x) { return x == f[x] ? x : (f[x] = found(f[x])); } void kruskal() { for (int i = 1; i <= m; i++) { int x = found(e[i].x), y = found(e[i].y); if (x == y) continue; f[x] = y; ans += e[i].d; if (++cnt == n-1) return;//最小生成树上最多n-1条边 } } int main() { scanf("%d%d", &n, &m); for (int i = 1; i <= m; i++) scanf("%d%d%d", &e[i].x, &e[i].y, &e[i].d); sort(e + 1, e + m + 1);//排序 for (int i = 1; i <= n; i++) f[i] = i;//并查集初始化 kruskal(); if (cnt == n-1) printf("%d ", ans); else puts("orn");//奇奇怪怪的输出,其实他没有这个测试点 return 0; }
prim
-
Prim算法思想类似于Dijkatra。
维护一个数组 d[x] 节点 x 与已确定是最小生成树集合中的节点之间权值最小的边的权值
每次从未标记的节点中选出 d 值最小的点,将其标记,在更新其他点的 d 值 -
时间复杂度为(O(n^2)),使用堆优化可以到(O(mlogn))(这里写的是堆优化后的板子) 但是这样不如Kruskal方便,
因此,Prim主要用于稠密图,尤其是完全图的求解 -
code
#include <queue> #include <cstdio> #include <cstring> using namespace std; const int N = 5005, M = 2e5+5; struct side{ int t, d, next; }e[M<<1]; int head[N], tot; void add(int x, int y, int z) { e[++tot].next = head[x]; head[x] = tot; e[tot].t = y; e[tot].d = z; } priority_queue< pair<int, int> > q; int n, m, d[N], ans, cnt; bool v[N]; void prim() { memset(d, 0x3f, sizeof(d)); q.push(make_pair(d[1] = 0, 1)); while (!q.empty() && cnt < n) { int x, w; x = q.top().second; w = -q.top().first; q.pop(); if (v[x]) continue; v[x] = 1; ans += w; cnt++; for (int i = head[x]; i; i = e[i].next) { int y = e[i].t; if (d[y] > e[i].d) { d[y] = e[i].d; q.push(make_pair(-d[y], y)); } } } } int main() { scanf("%d%d", &n, &m); while (m--) { int x, y, z; scanf("%d%d%d", &x, &y, &z); add(x, y, z); add(y, x, z); } prim(); if (cnt == n) printf("%d ", ans); else puts("orn"); return 0; }