zoukankan      html  css  js  c++  java
  • Andrew 算法(构造凸包)

    简要

    这是一个基 (Graham) 思想的又一个算法,我个人认为其中复杂度的改进应该是在(sort)(cmp) 部分吧,
    (Graham)(cmp) 部分需要对两个点算出叉乘,甚至在某些情况还要算出两个点的距离,这一步骤应该相对而言复杂度是较高的。
    (Andrew) 很好的避免了这一步骤,直接通过对点的 (x, y) 的简单排序就可以实现一个 (nlogn) 的算法

    算法思想

    • 首先我们要对所有的点进行排序,通常情况下是按照,(X) 从小到大,如果 (X) 相等的话,按照 (Y) 从小到大。
      这里有一点我们必须明白,排序后,最前面和最后面的点一定是极点。

    • 接着就是进行 (Graham) 算法的重要的一步 (Scan)
      第一遍 (Scan) 我们从最左端的点出发,做一遍 (Scan) 我们可以得到整个凸包的下部分。
      第二遍 (Scan) 我们从最右端的点出发,做一遍 (Scan) 我们可以得到整个凸包的上部分。
      这两份 (Scan) 合并起来就是完整的凸包了。

    实现图例

    这是第一遍 (Scan),蓝色的代表曾经走过,但是因为与后面的点构成凸包矛盾而进行过回溯。红色的线代表这一趟扫描满足条件的级边,也就是构成凸包的下半部分的边。

    这是第二遍 (Scan),绿色的代表曾经走过,但是因为与后面的点构成凸包矛盾而进行过回溯。黑色的线代表这一趟扫描满足条件的级边,也就是构成凸包的上半部分的边。

    时间复杂度分析

    (sort) 的时间是 (O(nlogn)) 两趟 (scan) 的时间都是线性的,整地复杂度是 (O(nlogn)),但是我们认为这个算法的时间复杂度是优于 (graham) 的,原因应该是我在开头提到的把

    模板题

    P2742 [USACO5.1]圈奶牛Fencing the Cows /【模板】二维凸包

    学习过程中(debug)很乱的代码

    /*
        Code by lifehappy 2020:04:17
        凸包Andrew算法
    */
    #include<bits/stdc++.h>
    using namespace std;
    const double INF = 1e100;
    const double eps = 1e-8;
    const double pi = acos(-1.0);
    const int N = 1e5 + 10;
    
    int n, cnt, m;
    
    int sgn(double x) {
        if(fabs(x) < eps)   return 0;
        if(x > 0)   return 1;
        return -1;
    }
    struct point {
        double x, y;
        point(double a = 0.0, double b = 0.0) : x(a), y(b) {}
        bool operator < (point t) {
            if(sgn(x - t.x) == 0)    return y < t.y;
            return x < t.x;
        }
    }p[N], ans[N], all[N];
    
    point operator - (point a, point b) {
        return point(a.x - b.x, a.y - b.y);
    }
    double dis(point a, point b) {
        a = a - b;
        return sqrt(a.x * a.x + a.y * a.y);
    }
    double cross(point a, point b) {
        return a.x * b.y - a.y * b.x;
    }
    
    void Andrew() {
        sort(p, p + n);
        int p1 = 0, p2;
        for(int i = 0; i < n; i++) {
            while(p1 > 1 && sgn(cross(ans[p1 - 1] - ans[p1 - 2], p[i] - ans[p1 - 2])) == -1)   p1--;
            ans[p1++] = p[i];
        }
        // cout << p1 << endl;
        // for(int i = 0; i < p1; i++) {
        //     int flag = 1;
        //     for(int j = 0; j < m; j++)
        //         if(sgn(ans[i].x - all[j].x) == 0 && sgn(ans[i].y - all[j].y) == 0) {
        //             flag = 0;
        //             break;
        //         }
        //     printf("%lf %lf    %s
    ", ans[i].x, ans[i].y, flag ? "False" : "            True");
        // }
        // cout << p1 << endl;
        p2 = p1;
        for(int i = n - 2; i>= 0; i--) {
            while(p2 > p1 && sgn(cross(ans[p2 - 1] - ans[p2 - 2], p[i] - ans[p2 - 2])) == -1)  p2--;
            ans[p2++] = p[i];
            // cout << p2 << endl;
        }
        // for(int i = 0; i < p2; i++)
        //     printf("%lf %lf
    ", ans[i].x, ans[i].y);
        // p2--;
        p2--;
        // for(int i = p1; i < p2; i++) {
        //     int flag = 1;
        //     for(int j = 0; j < m; j++)
        //         if(sgn(ans[i].x - all[j].x) == 0 && sgn(ans[i].y - all[j].y) == 0) {
        //             flag = 0;
        //             break;
        //         }
        //     printf("%lf %lf    %s
    ", ans[i].x, ans[i].y, flag ? "False" : "            True");
        // }
        // cout << p2 << endl;
        double target = 0.0;
        for(int i = 0; i < p2; i++)
            target += dis(ans[i], ans[i + 1]);
        printf("%.2f
    ", target);
    }
    int main() {
        // freopen("in.txt", "r", stdin);
        // freopen("out.txt", "w", stdout);
        // scanf("%d", &m);
        // for(int i = 0; i < m; i++)
        //     scanf("%lf %lf", &all[i].x, &all[i].y);
        scanf("%d", &n);
        for(int i = 0; i < n; i++)
            scanf("%lf %lf", &p[i].x, &p[i].y);
        Andrew();
        // point a(-9934.480000, -2886.200000), b(-9595.260000, -1905.480000), c(-9124.100000, -4804.250000);
        // printf("%lf
    ", cross(b - a, c - a));
        return 0;
    }
    

    较为简洁的代码

    /*
        Code by lifehappy 2020:04:17
        凸包Andrew算法
    */
    #include<bits/stdc++.h>
    using namespace std;
    const double INF = 1e100;
    const double eps = 1e-8;
    const double pi = acos(-1.0);
    const int N = 1e5 + 10;
    
    int n, cnt, m;
    
    int sgn(double x) {
        if(fabs(x) < eps)   return 0;
        if(x > 0)   return 1;
        return -1;
    }
    struct point {
        double x, y;
        point(double a = 0.0, double b = 0.0) : x(a), y(b) {}
        bool operator < (point t) {
            if(sgn(x - t.x) == 0)    return y < t.y;
            return x < t.x;
        }
    }p[N], ans[N], all[N];
    
    point operator - (point a, point b) {
        return point(a.x - b.x, a.y - b.y);
    }
    double dis(point a, point b) {
        a = a - b;
        return sqrt(a.x * a.x + a.y * a.y);
    }
    double cross(point a, point b) {
        return a.x * b.y - a.y * b.x;
    }
    
    void Andrew() {
        sort(p, p + n);
        int p1 = 0, p2;
        for(int i = 0; i < n; i++) {
            while(p1 > 1 && sgn(cross(ans[p1 - 1] - ans[p1 - 2], p[i] - ans[p1 - 2])) == -1)   p1--;
            ans[p1++] = p[i];
        }
        p2 = p1;
        for(int i = n - 2; i>= 0; i--) {
            while(p2 > p1 && sgn(cross(ans[p2 - 1] - ans[p2 - 2], p[i] - ans[p2 - 2])) == -1)  p2--;
            ans[p2++] = p[i];
        }
        p2--;
        double target = 0.0;
        for(int i = 0; i < p2; i++)
            target += dis(ans[i], ans[i + 1]);
        printf("%.2f
    ", target);
    }
    int main() {
        // freopen("in.txt", "r", stdin);
        // freopen("out.txt", "w", stdout);
        scanf("%d", &n);
        for(int i = 0; i < n; i++)
            scanf("%lf %lf", &p[i].x, &p[i].y);
        Andrew();
        return 0;
    }
    
  • 相关阅读:
    python——socket,IO多路复用(select),socket server实现多并发
    python——多线程,多进程,协程
    python——装饰器,迭代器,生成器
    time模块,datetime模块
    re模块,paramiko模块
    Freemaker中使用中括号来包含标签
    Freemaker中使用中括号来包含标签
    freemarker Velocity获取request,session
    freemarker Velocity获取request,session
    freemarker Velocity获取request,session
  • 原文地址:https://www.cnblogs.com/lifehappy/p/12721746.html
Copyright © 2011-2022 走看看