• AtCoder ARC 076D


    传送门: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
    ", mst());
        return 0;
    }
  • 相关阅读:
    JS 寻路算法
    Fireworks基本使用
    html基础知识汇总(二)之Emmet语法
    JS函数式编程
    Web前端导航
    CSS样式一
    选择器的分类
    框架集
    表单标签元素
    图像热点&图像映射
  • 原文地址:https://www.cnblogs.com/siuginhung/p/7751301.html
Copyright © 2020-2023  润新知