最小生成树(MST)[简述][模板]
Prim(添点法)
1. 任选一点(一般选1), 作为切入点,设其与最小生成树的距离为0(实际上就是选一个点,将此树实体化),.
2. 在所有未选择的点中选出与最小生成树距离最短的, 累计其距离, 并标为已选. 若都选择了, 则得到了最小生成树(的总路长).
3. 更新与此点相邻的点"与最小生成树的距离".返回2.
#include <cstdio> #include <cstring> #include <vector> #include <algorithm> using namespace std; #define N 1003 #define inf 0x3f3f3f3f struct node { int v, w; node () {} node(int _v, int _w) : v(_v), w(_w) {} }; vector<node> g[N]; int n, m, d[N]; bool vis[N]; int prim() { memset(vis, false, sizeof(vis)); memset(d, 0x3f, sizeof(d)); int ans = d[1] = 0; for (int i=0; i<n; i++) { int k = 0, mi = inf; for (int j=1; j<=n; j++) if (!vis[j] && d[j] < mi) mi = d[j], k = j; if (k == 0) break; vis[k] = true; ans += mi; for (int j=0, u; j<g[k].size(); j++) if (!vis[u = g[k][j].v] && d[u] > g[k][j].w) d[u] = g[k][j].w;///和Dijkstra很像,只是这里由松弛操作改成了更新 }///此处的d表示与树的距离 return ans;///返回的是最小生成树的边长和 } int main() { while (scanf("%d%d", &n, &m) == 2) { for (int i=0; i<=n; i++) g[i].clear(); for (int i=0, a, b, c; i<m; i++) { scanf("%d%d%d", &a, &b, &c); g[a].push_back(node(b, c)); g[b].push_back(node(a, c)); } printf("%d\n", prim()); } return 0; }
Kruskal(添边法)
1. 将所有点加入并查集, 每个点都是独立的集合
2. 将所有边按长度排序.
3. 拿出最小边, 判断两顶点是否在同一集合, 直到边集为空. 若是, 舍弃, 返回3. 若不是, 将左顶点加入右顶点之集合(左右都是无所谓的~), 返回3.
4. 若边集为空,则得到最小生成树.
并查集按惯例使用路径压缩.
#include <cstdio> #include <cstring> #include <algorithm> #include <vector> using namespace std; #define N 1002 struct node { int u, v, w; node() {} node(int _u, int _v, int _w):u(_u), v(_v), w(_w) {} }; vector<node> edge; int n, m, f[N]; bool cmp(const node &x, const node &y) { return x.w < y.w; } int find_set(int x) { if (f[x] == x) return x; return f[x] = find_set(f[x]); } int Kruskal() { sort(edge.begin(), edge.end(), cmp); for (int i=1; i<=n; i++) f[i] = i;///将所有点加入并查集,自己是一个独立的集合 int ans = 0; for (int i=0, u, v, w; i<edge.size(); i++) {///排序之后只要挨着拿就行 u = edge[i].u, v = edge[i].v, w = edge[i].w; u = find_set(u), v = find_set(v); if (u == v) continue; f[u] = v;///这个是随便的,虽然会引起效率上的不稳定,但是一次路径压缩之后就都一样了.. ans += w; } return ans; } int main() { while (scanf("%d%d", &n, &m) == 2) { edge.clear(); for (int i=0, a, b, c; i<m; i++) { scanf("%d%d%d", &a, &b, &c); edge.push_back(node(a, b, c));///两端点是平等的,插入一次即可 } printf("%d\n", Kruskal()); } return 0; }