最小生成树(基础版)
Part 1.什么是最小生成树
有一个问题:在一个无向图中,有
Part 2.kruskal
对于这个问题,我们可以对所有边的权值进行排序,挨个遍历每一条边,假设一开始没有任何的边,如果当前这一条边连接了一个没有任何一条边的点,那么连上这条边,更新答案,并将这个点标记为已连接。
由于需要判断当前节点是否在已生成的树中,所以要使用并查集。
kruskal 算法一般情况下时间复杂度为
Part 3.Prim
Prim 算法采用了和 dijkstra 一样的“蓝白点思想”,初始时先将所有节点标记为未连接,然后选取未选择的最小边权的边,如果这个边连接了至少一个未连接的节点,那么更新答案,将这条边所连接的两个节点标记为已连接。
一般情况下,Prim 算法的时间复杂度为
可以使用堆对其优化,堆优化后的 Prim 算法时间复杂度为
Prat 4.Boruvka
(此内容源自 OI Wiki)
Boruvka 算法可以看做是 Prim 算法和 kruskal 算法的结合体,它可以用来求解无向图的最小生成森林。
在边具有较多特殊性质的问题中,Boruvka 算法具有优势。
为了描述该算法,我们需要引入一些定义:
-
定义
E' 为我们当前找到的最小生成森林的边。在算法执行过程中,我们逐步向E' 加边,定义 连通块 表示一个点集V'\subseteq V ,且这个点集中的任意两个点u ,v 在E' 中的边构成的子图上是连通的(互相可达)。- 定义一个连通块的 最小边 为它连向其它连通块的边中权值最小的那一条。
初始时,
-
计算每个点分别属于哪个连通块。将每个连通块都设为「没有最小边」。
-
遍历每条边
(u, v) ,如果u 和v 不在同一个连通块,就用这条边的边权分别更新u 和v 所在连通块的最小边。 -
如果所有连通块都没有最小边,退出程序,此时的
E' 就是原图最小生成森林的边集。否则,将每个有最小边的连通块的最小边加入E' ,返回第一步。
当原图连通时,每次迭代连通块数量至少减半,算法只会迭代不超过
Part 5.例题1
题目
何老板买了一栋别墅。这栋别墅总共有
何老板经常拖欠农民工工资。之前好几次工作的工资何老板都还没发给这个电工。于是这个电工想报复何老板。这个电工决定做下面事情:
-
让何老板花的钱尽可能多。
-
让每个房间都连上网络(让何老板感觉他很好地完成了工作)。
-
让所有连线不构成环(如果有环会被何老板发现)。
问:电工最多让何老板花费多少?(若无法用网线将所有房间连接起来,就输出
解法
考虑使用 kruskal 算法,并查集,结构体存图,排序此处不再赘述。
定义一个数组存这个节点是否连接上了网络,然后判断当前节点是否已连上,如果不是,更新答案并将此节点标记为已连接。
参考代码:
#include<bits/stdc++.h>
using namespace std;
int n,m,ans,f[1005],cnt,a,b,c;
struct nide{
int x,y,qian;
}lj[100005];
bool cmp(nide X,nide Y){
return X.qian>Y.qian;
}
int getfather(int x){
if(x!=f[x])f[x]=getfather(f[x]);
return f[x];
}
int main(){
cin>>n>>m;
for(int i=1;i<=m;i++){
cin>>a>>b>>c;
lj[++cnt].x=a;
lj[cnt].y=b;
lj[cnt].qian=c;
}
sort(lj+1,lj+1+cnt,cmp);
for(int i=1;i<=n;i++)f[i]=i;
for(int i=1,p=0;i<=cnt&&p<m-1;i++){
int xx=getfather(lj[i].x);
int yy=getfather(lj[i].y);
if(xx!=yy){//如果当前边没连上网络
p++;//当前已选边数加一
ans+=lj[i].qian;//更新答案
f[xx]=yy;//将此节点标记为已连接
}
if(p>=n-1){//如果选的边数够了,输出答案并结束程序
printf("%d",ans);
return 0;
}
}
cout<<"-1";
return 0;
}
Part 6.例题2
题目
国防部想要通过无线网络与北极地区的一些军事哨所建立通讯连接。有两种不同的通讯技术将要被用来建立这个网络系统:无线电和卫星电话。
每个哨所都将配置无线电收发器。只有部分哨所将配置卫星电话。任意两个配置了卫星电话的哨所可以通过卫星来通信,卫星通信不受地域和距离的限制。但是任意两个哨所想要通过无线电来通信的话,就有距离限制了,两者的距离不能超过
输入格式
第一行两个整数
解法
首先这个题只告诉了我们每个哨所的坐标,所以需要我们用勾股定理来计算每两个哨所之间最短的距离(直线距离),接下来考虑使用 kruskal 算法,但是有一个值得注意的地方,在这个题中,所有的哨所都可以使用卫星电话,卫星电话有
代码
#include<bits/stdc++.h>
using namespace std;
struct lilaoshi{
long long x,y;
double qian;
}a[2500005];
long long f[5000005],n,m,k,cnt,p,A[5000005],B[5000005];
double ans;
long long zhaobaba(long long xx){
if(xx!=f[xx])f[xx]=zhaobaba(f[xx]);
return f[xx];
}
bool cmp(lilaoshi X,lilaoshi Y){
return X.qian<Y.qian;
}
int main(){
scanf("%lld%lld",&n,&m);
for(long long i=1;i<=m;i++)f[i]=i;
for(long long i=1;i<=m;i++)scanf("%lld%lld",&A[i],&B[i]);
for(long long i=1;i<m;i++){
for(long long j=i+1;j<=m;j++){
a[++cnt].x=i;
a[cnt].y=j;
//勾股定理求距离
a[cnt].qian=sqrt((A[i]-A[j])*(A[i]-A[j])+(B[i]-B[j])*(B[i]-B[j]));
}
}
sort(a+1,a+1+cnt,cmp);
for(long long i=1;i<=cnt;i++){
long long fx=zhaobaba(a[i].x);
long long fy=zhaobaba(a[i].y);
if(fx!=fy){
p++;
ans=max(ans,a[i].qian);
f[fx]=fy;
}
if(p==m-n)break;
//剩下的哨所用卫星电话,不需要通信线路
}
printf("%.2lf",ans);
return 0;
}
完结撒花!
(如果你还不懂,就评论区见吧)