凸包:通俗来讲,就是能够把集合中的点包围在内部的凸多边形
Andrew算法:是Graham算法的改进版,后者复杂度为O(nlogn),而这种算法复杂度为O(n)
这种算法的思想是先对坐标点进行排序,规则是按X从小到大进行排序,如果X相同则按Y从小到大排序
这一段代码如下:
struct Point{
int x,y;
};
Point P[maxn];
bool cmp(Point x,Point y)
{
return x.x<y.x||(x.x==y.x&&x.y<y.y);//x从小到大排序,如果x相同则y从小到大排序
}
int main(){
sort(p,p+n,cmp);
}
然后对于P1,P2,……,Pn这n个有序点进行扫描,具体方法为:
先将P1,P2放入凸包的边,然后从第三个点开始,不断判断Pi是否能加入凸包的边,如果不能,就删除Pi-1,再进行判断,还不行就继续删除,直到剩P1一个点,行的话就继续往下判断
如图所示,P1,P2先放入边,加入P3,再加入P4后用叉积判断发现P2,P3,P4构不成边(凹进去了,用叉积判断则表现为P4不在直线P2P3右方),于是删除P3,再判断,发现行了,那么继续往下找。
如图所示,找到最后一个点时,凸包的半个壳就出来了,再反过来判断一边,下半个壳也就出来了,这就是Andrew的核心步骤.
完整代码如下:
#include<bits/stdc++.h>
#define maxn 102
using namespace std;
struct Point{
int x,y;
};
Point p[maxn],ch[maxn]; //后者记录凸包上的点
bool cmp(Point x,Point y)
{
return x.x<y.x||(x.x==y.x&&x.y<y.y);//x从小到大排序,如果x相同则y从小到大排序
}
int Cross(Point x,Point y,Point z)
{
int x1=x.x-y.x;
int y1=x.y-y.y;
int x2=z.x-y.x;
int y2=z.y-y.y;
if((x1*y2-x2*y1)<=0) return 0;//如果不希望在凸包的边上有输入点。把<=改成<
return 1;
}
int andrew(int n)
{
sort(p,p+n,cmp);
int m=0;
int i;
for(i=0;i<n;i++)//从左到右扫描
{
while( m>1 && !Cross(ch[m-1],ch[m-2],p[i])) m--;
ch[m++]=p[i];
}
int k=m;
for(i=n-2;i>=0;i--)//从右到左扫描
{
while( m>k && !Cross(ch[m-1],ch[m-2],p[i])) m--;
ch[m++]=p[i];
}
if(n>1) m--;//凸包有m个顶点
return m;
}
int main(){
int n;
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%d %d",&p[i].x,&p[i].y);
// int ans=andrew(n),ans 为凸包顶点,ch数组为凸包顶点坐标数组
return 0;
}