计算几何之凸包
一、什么是凸包?
假设平面上有p0~p12共13个点,过某些点作一个多边形,使这个多边形能把所有点都“包”起来。
当这个多边形是凸多边形的时候,我们就叫它“凸包”。
二、凸包问题
我们把这些点放在二维坐标系里面,那么每个点都能用 (x,y) 来表示。
现给出点的数目和各个点的坐标。求构成凸包的点?
三、Graham扫描法
1、基本思路
(1)找到在平面中最左下方的那个点 p0;(先找纵坐标最小的点)
(2)计算各个点相对于 P0 的幅角 α ,按从小到大的顺序对各个点排序。
当 α 相同时,距离 P0 比较近的排在前面。
我们由几何知识可以知道,结果中第一个点和最后一个点一定是凸包上的点。
(3)建立一个栈,初始时 P0 P1 进栈,对于 P[3..n-1]的每个点,若栈顶的两个点与它不构成
"向左转"的关系,则将栈顶的点出栈,直至没有点需要出栈以后将当前点进栈;
2、代码实现
#include<cstdio> #include<cmath> #include<algorithm> #include<iostream> using namespace std; struct Point { double x,y; Point(double x=0,double y=0):x(x),y(y){} }; typedef Point Vector ; Vector operator + (Vector A,Vector B) { return Vector(A.x+B.x,A.y+B.y); } Vector operator - (Point A,Point B) { return Vector((A.x-B.x),(A.y-B.y)); } double Dot (Vector A,Vector B) { return A.x*B.x+A.y*B.y; } double Length(Vector A) { return sqrt(Dot(A,A)); } double Angle(Vector A,Vector B) { return acos(Dot(A,B)/Length(A)/Length(B)); } double Cross(Vector A,Vector B) { return A.x*B.y-A.y*B.x; } double Area2(Point A,Point B,Point C) { return Cross(B-A,C-A); } Point P[1005]; int stack[1005],top; bool cmp(Point A,Point B) { Vector AA=A-P[0]; Vector BB=B-P[0]; double ans=Cross(AA,BB); if(ans>0) return true; else if(ans==0) { if(A.x<B.x) return true; else return false; } else return false; } void init(int n) { int k; Point P0; scanf("%lf%lf",&P[0].x,&P[0].y); P0=P[0]; k=0; for(int i=1;i<n;i++) { scanf("%lf%lf",&P[i].x,&P[i].y); if( (P0.y>P[i].y) || (P[i].x<P0.x&&P0.y == P[i].y) ) { P0=P[i]; k=i; } } P[k]=P[0]; P[0]=P0; sort(P+1,P+n,cmp); } void Graham(int n) { if(n==1) {top=0;stack[0]=0;} if(n==2) { top=1; stack[0]=0; stack[1]=1; } if(n>2) { top=1; stack[0]=0; stack[1]=1; for(int i=2;i<n;i++) { while(top>0&&Cross(P[stack[top]]-P[stack[top-1]],P[i]-P[stack[top-1]])<=0) top--; top++; stack[top]=i; } } }
你若是天才,我便是疯子