看完之后推荐再看一看【最小生成树之Prim算法】-C++
定义:一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边。最小生成树可以用kruskal(克鲁斯卡尔)算法或Prim(普里姆)算法求出。 。
在一给定的无向图G = (V, E) 中,(u, v) 代表连接顶点 u 与顶点 v 的边(即),而 w(u, v) 代表此边的权重,若存在 T 为 E 的子集(即)且为无循环图,使得
的 w(T) 最小,则此 T 为 G 的最小生成树。
最小生成树其实是最小权重生成树的简称。
许多应用问题都是一个求无向连通图的最小生成树问题。例如:要在n个城市之间铺设光缆,主要目标是要使这 n 个城市的任意两个之间都可以通信,但铺设光缆的费用很高,且各个城市之间铺设光缆的费用不同;另一个目标是要使铺设光缆的总费用最低。这就需要找到带权的最小生成树。
这一章主要介绍Kruskal算法。
Kruskal算法的时间复杂度:O(m*log(n))(点数n边数m)
主要思路:
输入之后对边权值进行排序,然后按边权值从小到大进行合并(merge)操作,如果操作成功(被合并的两个点不在一棵树上),则把这两个顶点的边权值加入总数,最后输出total即可。
主要使用:
“并查集。”
洛谷P3366【模板】最小生成树
这道题我第一次是用Kruskal来写的,具体思路再讲解一下。
首先把get和merge函数写好,为了方便,我把merge写成了bool类型:如果成功合并(要求合并的两个顶点不在一棵树上)就返回true。
然后是最正常的运用结构体进行循环读入,读入完成之后写cmp排序函数按边权值从小到大进行排序。
接下来才和并查集扯上关系,所以要重新定义fa数组,然后进行初始化;
核心代码
int cnt=0; int total=0; for(int i=1;i<=p;i++)//p为边数 { if(merge(mp[i].u,mp[i].v)) { cnt++; total+=mp[i].w; if(cnt==p-1) break; } }
这段代码主要是为了统计权值和。把权值从最小到最大跑一遍,如果能够合并就合并然后加进total即可。然后就没什么难的了emm。
下面贴代码;
参考代码:
#include<bits/stdc++.h> using namespace std; struct noded { int u,v; int w; noded(){} noded(int uu,int vv,int ww) { u=uu,v=vv,w=ww; } }mp[200010]; bool cmp(noded x,noded y) { return x.w<y.w; } int fa[5010]; int get(int x) { if(fa[x]==x)return x; else { fa[x]=get(fa[x]); return fa[x]; } } bool merge(int x,int y) { int r1=get(x),r2=get(y); if(r1!=r2) { fa[r1]=r2; return true; } else return false; } int ans[250010]; void init() { for(int i=1;i<=5000;i++) { fa[i]=i; } } int main() { //sqrt(pow((x1-x2),2)+pow((y1-y2),2)); int n,p; cin>>n>>p; for(int i=1;i<=p;i++) { cin>>mp[i].u>>mp[i].v>>mp[i].w; } sort(mp+1,mp+1+p,cmp); //for(int i=1;i<=k;i++) //{ // cout<<endl<<mp[i].w; //} init(); int cnt=0; int total=0; for(int i=1;i<=p;i++) { if(merge(mp[i].u,mp[i].v)) { cnt++; total+=mp[i].w; if(cnt==p-1) break; } } cout<<total<<endl; return 0; }
ov.
来源:https://www.cnblogs.com/moyujiang/p/11167775.html