【问题描述】 2015年,全中国实现了户户通电。作为一名电力建设者,小明正在帮助一带一路上的国家通电。 这一次,小明要帮助 n 个村庄通电,其中 1 号村庄正好可以建立一个发电站,所发的电足够所有村庄使用。 现在,这 n 个村庄之间都没有电线相连,小明主要要做的是架设电线连接这些村庄,使得所有村庄都直接或间接的与发电站相通。 小明测量了所有村庄的位置(坐标)和高度,如果要连接两个村庄,小明需要花费两个村庄之间的坐标距离加上高度差的平方,形式化描述为坐标为 (x_1, y_1) 高度为 h_1 的村庄与坐标为 (x_2, y_2) 高度为 h_2 的村庄之间连接的费用为: sqrt((x_1-x_2)(x_1-x_2)+(y_1-y_2)(y_1-y_2))+(h_1-h_2)*(h_1-h_2)。 在上式中 sqrt 表示取括号内的平方根。请注意括号的位置,高度的计算方式与横纵坐标的计算方式不同。 由于经费有限,请帮助小明计算他至少要花费多少费用才能使这 n 个村庄都通电。 【输入格式】 输入的第一行包含一个整数 n ,表示村庄的数量。 接下来 n 行,每个三个整数 x, y, h,分别表示一个村庄的横、纵坐标和高度,其中第一个村庄可以建立发电站。 【输出格式】 输出一行,包含一个实数,四舍五入保留 2 位小数,表示答案。 【样例输入】 4 1 1 3 9 9 7 8 8 6 4 5 4 【样例输出】 17.41 【评测用例规模与约定】 对于 30% 的评测用例,1 <= n <= 10; 对于 60% 的评测用例,1 <= n <= 100; 对于所有评测用例,1 <= n <= 1000,0 <= x, y, h <= 10000。
思路分析:题目的意思大概是说:现在我有n个结点,每个结点都有三个属性,通过两个结点的三个属性可以算出这两个结点相连边的权值大小。题目要求所有n个村庄全部通电,就是让所有点都连通起来,而又要求代价最小,其实就是求该无向网的最小生成树。求最小生成树的算法常见的有prim算法和Kruskal算法,我比较熟悉后一种,所以这里就选择用Kruskal算法来解决该问题。 Kruskal算法的具体讲解可参考:【灾后重建】蓝桥杯第六届省赛C/C++大学A组(并查集+Kruskal算法)
这里就直接上代码了:
#include<bits/stdc++.h> using namespace std; const int maxn = 1002; int n, par[maxn]; //定义结点 struct node { int x; int y; int h; }nodes[maxn]; //定义边 struct edge { int start; int end; float cost; }edges[maxn * (maxn - 1) / 2]; //计算代价 float cost(node a, node b) { float x = a.x - b.x; float y = a.y - b.y; float h = a.h - b.h; return sqrt(x * x + y * y) + h * h; } //并查集的一些操作 //求根结点 int get_root(int a) { if(par[a] != a) { par[a] = get_root(par[a]); } return par[a]; } //查询是否在同一集合中 bool query(int a,int b) { return get_root(a) == get_root(b); } //合并两个结点 void merge(int a,int b) { par[get_root(a)] = get_root(b); } //初始化 void init(int n) { for(int i = 1;i <= n;i++) { par[i] = i; } } bool cmp(edge x, edge y) { return x.cost < y.cost; } int main() { cin >> n; init(n); int x, y, h; int index = 0; for(int i = 0; i < n; i++) { cin >> nodes[i].x >> nodes[i].y >> nodes[i].h; } //生成所有边的序号,其实就是组合数 for(int i = 0; i < n; i++) { for(int j = 0; j < n && j != i; j++) { edges[index].start = i; edges[index].end = j; edges[index].cost = cost(nodes[i], nodes[j]); index++; } } //按cost从小到大排序 sort(edges, edges + index, cmp); float sum = 0.0; int cnt = 0; //在不形成环的条件下依次选择n - 1条边,并把它们的权值相加输出 for(int i = 0; i < index; i++) { int start = edges[i].start; int end = edges[i].end; if(get_root(start) != get_root(end)) { cnt++; sum += edges[i].cost; if(cnt == n - 1) { break; } } } printf("%.2f", sum); return 0; }