通讯网络
题目内容
(通讯网络)某地区共有n座村庄,每座村庄的坐标(x,y)已知,现在要在村庄之间建立通讯网络。通讯工具有两种,分别是需要铺设的普通线路和无线通讯的卫星设备。只能给k个村庄配备卫星设备,拥有卫星设备的村庄互相间可直接通讯。铺设了线路的村庄之间也可以通讯。但是由于技术原因,两个村庄之间线路长度最多不能超过 d, 否则就会由于信号衰减导致通讯不可靠。要想增大 d 值,则会导致要投入更多的设备(成本)。如何分配卫星设备,才能使各个村庄之间能直接或间接的通讯,并且 d 的值最小?
文献查阅情况
给定一个无向图,如果它任意两个顶点都联通并且是一棵树,那么我们就称之为生成树(Spanning Tree)。如果是带权值的无向图,那么权值之和最小的生成树,我们就称之为最小生成树(MST, Minimum Spanning Tree)。
我们由最小生成树的定义,可以延伸出一个修建道路的问题:把无向图的每个顶点看作村庄,计划修建道路使得可以在所有村庄之间通行。把每个村庄之间修建道路的费用看作权值,那么我们就可以得到一个求解修建道路的最小费用的问题。
常见求解最小生成树的算法有Kruskal算法和Prim算法。由于篇幅问题再此对于Prim算法,就不多做解释了。现在我们看看Kruskal算法,是怎么来求解最小生成树的问题。
Kruskal算法描述
Kruskal算法是基于贪心的思想得到的。首先我们把所有的边按照权值先从小到大排列,接着按照顺序选取每条边,如果这条边的两个端点不属于同一集合,那么就将它们合并,直到所有的点都属于同一个集合为止。至于怎么合并到一个集合,那么这里我们就可以用到一个工具——-并查集(不知道的同学请移步:Here)。换而言之,Kruskal算法就是基于并查集的贪心算法。
Kruskal算法流程
对于图G(V,E),以下是算法描述:
输入: 图G
输出: 图G的最小生成树
具体流程:
(1)将图G看做一个森林,每个顶点为一棵独立的树
(2)将所有的边加入集合S,即一开始S = E
(3)从S中拿出一条最短的边(u,v),如果(u,v)不在同一棵树内,则连接u,v合并这两棵树,同时将(u,v)加入生成树的边集E’
(4)重复(3)直到所有点属于同一棵树,边集E’就是一棵最小生成树
问题解题思路
第一步建模,输入n个村庄坐标Xi,Yi,将村庄看做结点,建立无向完全图,
接着建立最小生成树
kruskal算法(避圈法)建立最小生成树
- 所有边按照权值非降次序排序
- 选取权值最小且没有回路的边,记录选取的边对应的结点
- 所有的结点都包含在树中即可结束,选取(n-1)条边
共需要边n-1条。由于只能k个村庄之间用卫星,即
最小生成树中最长的k-1条边需要删去,不铺设通讯线路,满足条件为:删
除后,剩余的边中,最长的边要<=d。由于卫星可以连接模块中任意村庄,
所以在建立最小生成树后,删除边的过程中,每删除一条边,需记下被删除
边的两个村庄的坐标,即为需要卫星相连接的村庄的坐标,输出卫星分配
##算法流程
using namespace std;
#include <math.h>//添加数学函数库
#define maxn 100
#include<stdio.h>
#include<algorithm>
typedef struct{
int x, y;
double w;
}edge;
edge e[maxn];
int rank_[maxn], father[maxn], sum;
int cmp(edge a, edge b){
//按权值非降序排序(相同则按x坐标)
if(a.w == b.w) return a.x < b.x;
return a.w < b.w;
}
void make_set(int x){
father[x] = x;
rank_[x] = 0;
}
int find_set(int x){
return x != father[x] ? find_set(father[x]) : father[x];
}
void union_set(int x, int y, int w){
sum += w;
if(x == y)