题目描述
某个宇宙帝国有 n 个星球,由于宇宙的空间是三维的,因此每个星球的位置可以用三维坐标来表示
(x,y,z) 。 任意两个不同的星球 i 和j 都有一条边相连,边的距离是这样计算的: disij=min(|xi-xj|,|yi-yj|,|zi-zj|) 。 其中| |符号表示取绝对值。现在让你来挑 n−1 条边,让这 n 个星球连通成一个最小生成树,输出构成最小生成树的n−1 条边的长度总和。
输入格式 1426.in
第一行,一个整数 n 。
1≤n≤100000
接下来有 n 行,每行三个整数:x , y ,z 。表示一个星球的坐标, −1000000000≤x,y,z≤1000000000 。 没有两个星球的位置完全重叠。
输出格式 1426.out
一行,构成最小生成树的 n−1 条边的长度总和。
输入样例 1426.in
5
11 -15 -15
14 -5 -15
-1 -1 -5
10 -4 -1
19 -4 19
输出样例 1426.out
4
首先回忆一下最小生成树问题的 Kruskal 算法:按耗费递增的顺序来考虑每条边,每次考虑一条边。当考虑某条边时,若将其加入到已选边的集合中会出现环路,则将其抛弃,否则,将它选入。
在本题中,如果直接枚举每两个星球之间的距离,再做一遍 Kruskal ,很显然是会 TLE 的。我们可以这样想:对于两条边
i
和
容易作个简单的推论:以
x
值为例,如果各星球按
又或者,直接回到从贪心算法的角度来说,我们的目标是让所有星球连通,且总费用最小。那么只要考虑如何选取边才能使费用最小。显然,最终目的是要让全部边连通,那么我们与其选择用差值更大的情况,不如用按某个值排序后相邻的边,这样的选取原则不会存在更优的方案。
综上所述,只需要分别按各星球的
x
、
时间主要花费在排序上,总的时间复杂度为
参考代码:
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
using namespace std;
const int maxn = 1e5 + 100;
struct Tplanet {
int x, y, z;
int id;
} planet[maxn];
struct Edge {
int u, v, w;
Edge () : u(0), v(0), w(0) {}
Edge (int x, int y, int z) : u(x), v(y), w(z) {}
bool operator < (const Edge x) const { return w < x.w; }
} edge[maxn * 3];
int n;
int fa[maxn];
bool byX(Tplanet i, Tplanet j) { return i.x < j.x; }
bool byY(Tplanet i, Tplanet j) { return i.y < j.y; }
bool byZ(Tplanet i, Tplanet j) { return i.z < j.z; }
int find(int root) { return fa[root] == root ? root : fa[root] = find(fa[root]); }
int main(void) {
freopen("1426.in", "r", stdin);
freopen("1426.out", "w", stdout);
scanf("%d", &n);
for (int i = 0; i < n; i++) {
scanf("%d%d%d", &planet[i].x, &planet[i].y, &planet[i].z);
planet[i].id = i;
fa[i] = i;
}
int cnt = 0;
sort(planet, planet + n, byX);
for (int i = 1; i < n; i++) edge[cnt++] = Edge(planet[i - 1].id, planet[i].id, planet[i].x - planet[i - 1].x);
sort(planet, planet + n, byY);
for (int i = 1; i < n; i++) edge[cnt++] = Edge(planet[i - 1].id, planet[i].id, planet[i].y - planet[i - 1].y);
sort(planet, planet + n, byZ);
for (int i = 1; i < n; i++) edge[cnt++] = Edge(planet[i - 1].id, planet[i].id, planet[i].z - planet[i - 1].z);
sort(edge, edge + cnt);
long long ans = 0;
for (int i = 0; i < cnt; i++) {
int fa_u = find(edge[i].u), fa_v = find(edge[i].v);
if (fa_u != fa_v) { fa[fa_u] = fa_v; ans += edge[i].w; }
}
printf("%lld\n", ans);
return 0;
}