[算法笔记]凸包问题
二维凸包问题 Andrew算法
写在前面:
本文参考自:刘汝佳《算法竞赛入门经典-训练指南》
介绍:
凸包:把给定点包围在内部的、面积最小的凸多边形。
Andrew算法是Graham算法的变种,速度更快,数值稳定性也更好。
算法实现:
- 首先把全部点按照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); }
- 把P1,P2放入凸包中,凸包中的点使用栈存储。
- 从P3开始,当下一个点在凸包当前前进方向左边的时候继续。
- 否则依次删除近期增加凸包的点,直到新点在左边。
如图,新点P18在当前前进方向P10P15的右边(使用叉积推断),因此须要从凸包上删除点P15和P10。让P8的下一个点为P18。反复该过程直到碰到最右边的Pn,求出下凸包即凸包的下轮廓。然后从Pn反过来反复该步骤求出上凸包,合并起来后就是完整的凸包。
该算法扫描为O(n)复杂度,排序O(nlogn)。
完整代码:
#includeusing 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; }