凸包算法的初步学习
所谓凸包问题是指给定一系列的点,求解出这些点中组合起来的能把所有其他点包裹起来的最小多边形。
预备知识是向量的点乘和方向判定。
方法是,首先找出纵坐标最小的点,若有相同最小纵坐标再取横坐标最小,如果有相同的点可以删掉一个;之后以选定的点为基准,按照每点与基准点的角度大小进行扫描,扫描规

以上图为例,首先以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;
}
浙公网安备 33010602011771号