凸包算法的初步学习
所谓凸包问题是指给定一系列的点,求解出这些点中组合起来的能把所有其他点包裹起来的最小多边形。
预备知识是向量的点乘和方向判定。
方法是,首先找出纵坐标最小的点,若有相同最小纵坐标再取横坐标最小,如果有相同的点可以删掉一个;之后以选定的点为基准,按照每点与基准点的角度大小进行扫描,扫描规
以上图为例,首先以A为基准点,首先插入B,可知B一定在凸包上,接下来扫描C...当扫描到G时,向量<C,H>,和向量<H,G>的点乘为正(逆时针扫描时判断是否为负,顺时针相反),则删掉H,回溯删掉所有符号不相同的点。扫描完一遍后,即可得到凸包。
其算法空间复杂度为O(1)。代码如下:
#include<stdio.h> #include<math.h> #include<algorithm> using namespace std; struct Point { double x,y,len; }Pt[20000],Stack[20000],Point_A; double Cross(Point a,Point b,Point c) { return (b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x); } double Dis(Point a,Point b) { return sqrt(pow(a.x-b.x,2)+pow(a.y-b.y,2)); } void FindPoint(int n) { int i,tempNumber=0; Point tempPoint; Point_A=Pt[0]; for(i=1;i<n;i++) { if(Pt[i].y<Point_A.y||Pt[i].y==Point_A.y&&Pt[i].x<Point_A.x) { tempNumber=i; Point_A=Pt[i]; } } tempPoint=Pt[0]; Pt[0]=Pt[tempNumber]; Pt[tempNumber]=tempPoint; } bool Cmp(Point a,Point b) { double k=Cross(Point_A,a,b); if(k>0) return true; if(k<0) return false; a.len=Dis(Point_A,a); b.len=Dis(Point_A,b); return a.len>b.len; } void Graham(int n) { int i,top=2; Pt[n]=Pt[0]; Stack[0]=Pt[0]; Stack[1]=Pt[1]; Stack[2]=Pt[2]; for(i=3;i<=n;i++) { while(Cross(Stack[top-1],Stack[top],Pt[i])<=0&&top>1) top--; Stack[++top]=Pt[i]; } } int main(void) { int i,Num; while(scanf("%d",&Num)!=EOF) { for(i=0;i<Num;i++) scanf("%lf%lf",&Pt[i].x,&Pt[i].y); FindPoint(Num); sort(Pt,Pt+Num,Cmp); Graham(Num); } return 0; }