AtCoder ARC 076D - Built?

传送门:http://arc076.contest.atcoder.jp/tasks/arc076_b

本题是一个图论问题——Manhattan距离最小生成树(MST)。

在一个平面网格上有n个格点,第i个格点的坐标是(xi,yi),构造一条连接点(a,b)和点(c,d)的边的代价是min{|a-c|,|b-d|}。对给定的n个格点构造连通图,使得总代价最小。

这是一个最小生成树(MST)问题。最“简单”的方法是,由n个结点构造一个无向完全图Kn,之后用Prim算法生成MST。这个程序的时间复杂度为O(n2logn),空间复杂度为O(n2)。对于105的数据规模,这个方法显然是不可取的。

考虑到Kruskal算法和Prim算法的时间复杂度均为O(ElogV),应尽可能地降低E的数量级(从Θ(n2)降至Θ(n))。

考虑以下的构造方式:对于点(a,b)和点(c,d),不构造代价为min{|a-c|,|b-d|}的边,而是构造两条边,其中一条边的代价为|a-c|,另一条边的代价为|b-d|。设存在i,j,k,使得xi<xj<xk,则连接ik的代价为|xi-xk|的边一定不会出现在MST中。因此,只需在坐标上相邻的两个结点之间构造边即可。这个构造方式构造的边数E=2(n-1),是Θ(n)的。具体的构造方式如下:

a.对结点按x坐标排序,在每一对相邻的点之间构造一条边,代价是相邻两点距离的x分量;

b.对结点按y坐标排序,在每一对相邻的点之间构造一条边,代价是相邻两点距离的y分量;

构造边的时间复杂度为O(nlogn),空间复杂度为O(n)。

之后,用Kruskal算法生成MST,时间复杂度为O(nlogn)。参考程序如下:

#include <bits/stdc++.h>
using namespace std;

#define MAX_N 100010

struct point {int id, x, y;};
struct edge {int u, v, w;};

int n;
point p[MAX_N];
edge edgeset[2 * MAX_N];

//disjoint set
int pa[MAX_N];
int rnk[MAX_N];

void init(void)
{
    memset(pa, 0, sizeof(pa));
    memset(rnk, 0, sizeof(rnk));
    for (int i = 0; i < n; i++) {
        pa[i] = i;
        rnk[i] = 0;
    }
}

int find(int x)
{
    if (pa[x] == x) return x;
    else return pa[x] = find(pa[x]);
}

void unite(int x, int y)
{
    x = find(x);
    y = find(y);
    if (x == y) return;
    if (rnk[x] < rnk[y]) pa[x] = y;
    else {
        pa[y] = x;
        if (rnk[x] == rnk[y]) rnk[x]++;
    }
}

bool same(int x, int y)
{
    return (find(x) == find(y));
}

int abs(int a)
{
    return a >= 0? a: -a;
}

bool cmp_x(point a, point b)
{
    return a.x < b.x;
}

bool cmp_y(point a, point b)
{
    return a.y < b.y;
}

bool cmp_edge(edge a, edge b)
{
    return a.w < b.w;
}

//kruskal minimum spanning tree
int mst(void)
{
    init();
    int res = 0;
    for (int i = 0; i < 2 * (n - 1); i++) {
        edge e = edgeset[i];
        if (!same(e.u, e.v)) {
            unite(e.u ,e.v);
            res += e.w;
        }
    }
    return res;
}

int main(void)
{
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d%d", &p[i].x, &p[i].y);
        p[i].id = i;
    }
    sort(p, p + n, cmp_x);
    for (int i = 0; i < n - 1; i++) {
        edgeset[i].u = p[i].id;
        edgeset[i].v = p[i + 1].id;
        edgeset[i].w = abs(p[i + 1].x - p[i].x);
    }
    sort(p, p + n, cmp_y);
    for (int i = 0; i < n - 1; i++) {
        edgeset[i + n - 1].u = p[i].id;
        edgeset[i + n - 1].v = p[i + 1].id;
        edgeset[i + n - 1].w = abs(p[i + 1].y - p[i].y);
    }
    sort(edgeset, edgeset + 2 * (n - 1), cmp_edge);
    printf("%d\n", mst());
    return 0;
}

 

posted on 2017-10-29 21:19  SiuGinHung  阅读(321)  评论(0编辑  收藏  举报

导航