[算法笔记]凸包问题


二维凸包问题 Andrew算法

写在前面:

本文参考自:刘汝佳《算法竞赛入门经典-训练指南》

介绍:

凸包:把给定点包围在内部的、面积最小的凸多边形。

Andrew算法是Graham算法的变种,速度更快,数值稳定性也更好。

算法实现:

  1. 首先把全部点按照x从小到大排序(x相等,则按照y从小到大排序),删除重复点后得到点序列P1...Pn
    bool operator<(const Point &p1, const Point &p2)//重载<,后续使用sort
    {
        return p1.x < p2.x || (p1.x == p2.x && p1.y < p2.y);
    }
  2. 把P1,P2放入凸包中,凸包中的点使用栈存储。
  3. 从P3开始,当下一个点在凸包当前前进方向左边的时候继续。
  4. 否则依次删除近期增加凸包的点,直到新点在左边。

如图,新点P18在当前前进方向P10P15的右边(使用叉积推断),因此须要从凸包上删除点P15和P10。让P8的下一个点为P18。反复该过程直到碰到最右边的Pn,求出下凸包即凸包的下轮廓。然后从Pn反过来反复该步骤求出上凸包,合并起来后就是完整的凸包。

该算法扫描为O(n)复杂度,排序O(nlogn)。

 

完整代码:

#include

using namespace std;

struct Point {
    double x, y;

    Point(double x = 0, double y = 0) : x(x), y(y){};
} p[10005], ch[10005];

int n;

bool operator<(const Point &p1, const Point &p2) {
    return p1.x < p2.x || (p1.x == p2.x && p1.y < p2.y);
}

Point operator-(Point A, Point B) {
    return Point(A.x - B.x, A.y - B.y);
}

double Cross(Point A, Point B) {//两点交叉积
    return A.x * B.y - A.y * B.x;
}

int ConvexHull() {
    sort(p, p + n);
    int m = 0;
    for (int i = 0; i < n; i++) {
        while (m > 1 && Cross(ch[m - 1] - ch[m - 2], p[i] - ch[m - 2]) <= 0)m--;
        ch[m++] = p[i];
    }
    int k = m;
    for (int i = n - 2; i >= 0; i--) {
        while (m > k && Cross(ch[m - 1] - ch[m - 2], p[i] - ch[m - 2]) <= 0)m--;
        ch[m++] = p[i];
    }
    if (n > 1)m--;
    return m;
}

int main() {
    cin >> n;
    for (int i = 0; i < n; i++) {
        double x, y;
        cin >> x >> y;
        p[i] = Point(x, y);
    }
    return 0;
}