最小生成树 洛谷 P3366
// 由于前两天打Orz杯队友卡在一道最优比例生成树01分数规划的题上,我帮忙debug也没AC。
// 反思自己不会使用Kruskal算法,特地来总结一番图论的基础算法:最小生成树。
题目背景:
求一个无向图的最小生成树,一般有两种算法,Prim 和 Kruskal 。
两种算法的原理和证明就这里略过了,详见《数据结构》教材 / 百度。
Prim算法:
简单讲,Prim 和 Kruskal 都是利用了贪心的思想。
朴素的 Prim 算法类似 Dijkstra :
- 初始时选择任意源点 Vnew = {u0};
- 每一轮找到权值最小的边<u, v>,且满足 u 在 Vnew内,v 不在Vnew内 ;
- 将边 <u, v> 加入生成树,将点 v 加入到的点集中,Vnew = Vnew ∪ {v};
- 直到 Vnew = V 程序结束。
AC代码(堆优化):
#include<cstdio> #include<queue> #include<cstring> using namespace std; const int maxn = 5010; const int maxm = 200010; typedef long long ll; struct Edge { int to, w, next; }e[maxm<<1]; int n, m; int head[maxn], tot; void add(int u,int v,int w) { e[++tot].to = v; e[tot].w = w; e[tot].next = head[u]; head[u] = tot; } typedef pair<int, int> pii; // <权值, 点> priority_queue<pii, vector<pii>, greater<pii> > q; int dis[maxn]; bool vis[maxn]; void prim() { int cnt = 0; // 已连通的点 ll ans = 0; // 最小生成树的权值 memset(dis, 0x3f, sizeof(dis)); dis[1] = 0; q.push(make_pair(0, 1)); while(!q.empty()&&cnt<n) { int d = q.top().first, u = q.top().second; q.pop(); if(vis[u]) continue; vis[u] = 1; ++cnt; ans += d; for(int i=head[u];i;i=e[i].next) { int v = e[i].to, w = e[i].w; if(w<dis[v]) { dis[v] = w; q.push(make_pair(dis[v], v)); } } } if(cnt==n) printf("%lld", ans); else printf("orz"); } int main() { scanf("%d %d", &n, &m); for(int i=0;i<m;i++) { int u, v, w; scanf("%d %d %d", &u, &v, &w); add(u, v, w); add(v, u, w); } prim(); return 0; }
附Dijkstra代码:
int d[maxn]; bool vis[maxn]; void dijkstra(int s) { memset(d, 0x3f, sizeof(d)); d[s] = 0; priority_queue<node>q; q.push(node(0, s)); while(!q.empty()) { node now = q.top(); q.pop(); int u = now.to; if(vis[u]) continue; vis[u] = 1; for(int i=0;i<G[u].size();i++) { int v = G[u][i].to, w = G[u][i].w; if(d[v]>d[u]+w) { d[v] = d[u] + w; q.push(node(d[v], v)); } } } }
Kruskal算法:
- E = {};
- 将所有边按照权值排序;
- 从小到大选择边 <u, v>,如果新加入的边不会产生环,则加入 E。
- 直到边集大小为 n - 1。
利用并查集可以很方便维护 u, v 节点所在的子树是否连通,所以代码很好实现。
AC代码:
#include<cstdio> #include<algorithm> using namespace std; const int maxn = 5010; const int maxm = 200010; struct Edge { int u, v, w; bool operator<(const Edge& a) const { return w < a.w; } } e[maxm]; int n, m; int fa[maxn]; int Find(int x) { return x==fa[x]?x:fa[x]=(Find(fa[x])); } bool Union(int x, int y) { int a = Find(x), b = Find(y); if(a!=b) { fa[a] = b; return true; } else return false; } int main() { scanf("%d %d", &n, &m); for(int i=1;i<=n;i++) fa[i] = i; for(int i=0;i<m;i++) { scanf("%d %d %d", &e[i].u, &e[i].v, &e[i].w); } // Kruskal sort(e, e+m); int k = 0; long long ans = 0; for(int i=0;i<m && k<n;i++) { if(Union(e[i].u, e[i].v)) { ans += e[i].w; ++k; } } if(k<n-1) { // 原图不连通 printf("orz\n"); } else { printf("%lld\n", ans); } return 0; }

浙公网安备 33010602011771号