[SMOJ1426]最小生成树

来源:互联网 发布:奥拉星淘宝怎么买号 编辑:程序博客网 时间:2024/06/16 07:03

题目描述

某个宇宙帝国有 n 个星球,由于宇宙的空间是三维的,因此每个星球的位置可以用三维坐标来表示 (x,y,z)。 任意两个不同的星球 ij 都有一条边相连,边的距离是这样计算的:disij=min(|xixj|,|yiyj|,|zizj|)。 其中| |符号表示取绝对值。现在让你来挑 n1 条边,让这 n 个星球连通成一个最小生成树,输出构成最小生成树的 n1 条边的长度总和。

输入格式 1426.in

第一行,一个整数 n。 1n100000
接下来有 n 行,每行三个整数: x, y, z。表示一个星球的坐标,1000000000x,y,z1000000000。 没有两个星球的位置完全重叠。

输出格式 1426.out

一行,构成最小生成树的 n1 条边的长度总和。

输入样例 1426.in

5
11  -15  -15
14  -5  -15
-1  -1  -5
10  -4  -1
19  -4  19

输出样例 1426.out

4


首先回忆一下最小生成树问题的 Kruskal 算法:按耗费递增的顺序来考虑每条边,每次考虑一条边。当考虑某条边时,若将其加入到已选边的集合中会出现环路,则将其抛弃,否则,将它选入。

在本题中,如果直接枚举每两个星球之间的距离,再做一遍 Kruskal ,很显然是会 TLE 的。我们可以这样想:对于两条边 ij ,它们之间的距离是它们坐标的三个值的差当中最小的。不妨猜想,如果边 (i,j) 出现在最终的最小生成树中,它们必定的 xyz 在分别按 xyz 排序后的序列中相邻。(有点拗口,但是应该没毛病)

容易作个简单的推论:以 x 值为例,如果各星球按 x 排序之后,选中的不是两个相邻的星球,而是两个不相邻的星球,那么一定可以把这条边去掉,用更优的方案替换。

又或者,直接回到从贪心算法的角度来说,我们的目标是让所有星球连通,且总费用最小。那么只要考虑如何选取边才能使费用最小。显然,最终目的是要让全部边连通,那么我们与其选择用差值更大的情况,不如用按某个值排序后相邻的边,这样的选取原则不会存在更优的方案。

综上所述,只需要分别按各星球的 xyz 值排序,每次将相邻的星球连边,这样一共有(星球数-1)*3条边。对这些边做 Kruskal 即可。

时间主要花费在排序上,总的时间复杂度为 O(nlog2n)

参考代码:

#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;}
0 0
原创粉丝点击