凸包:把给定点包围在内部的、面积最小的凸多边形。
Andrew算法是Graham算法的变种,速度更快稳定性也更好。
首先把所有点排序,按照第一关键字x第二关键字y从小到大排序,删除重复点后得到点序列P1...Pn。
1)把P1,P2放入凸包中,凸包中的点使用栈存储
2)从p3开始,当下一个点在凸包当前前进方向(即直线p1p2)左边的时候继续;
3)否则依次删除最近加入凸包的点,直到新点在左边。
如图,新点P18在当前前进方向P10P15的右边(使用叉积判断),因此需要从凸包上删除点P15和P10,让P8的下一个点为P18。重复该过程直到碰到最右边的Pn,求出下凸包即凸包的下轮廓。然后从Pn反过来重复该步骤求出上凸包,合并起来后就是完整的凸包。
该算法扫描为O(n)复杂度,排序O(nlogn)。
C++ code(刘汝佳《算法竞赛入门经典-训练指南》模板)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <cmath>
using namespace std;
const double eps = 1e-8,Pi=3.14159265;
int n;
inline int dcmp(double x)//三态函数
{
if(fabs(x) < eps) return 0;
else return x>0 ? 1 : -1;
}
#define Vector Point
struct Point
{
double x,y;
inline Point(double x=0,double y=0):x(x),y(y) {}
}p[10000+5],ch[10000+5];
bool myCmp(Point A, Point B)
{
if(A.x != B.x) return A.x < B.x;
else return A.y < B.y;
}
Vector operator + (Vector A, Vector B) {return Vector(A.x + B.x, A.y + B.y);}
Vector operator - (Vector A, Vector B) {return Vector(A.x - B.x, A.y - B.y);}
bool operator == (const Vector& A, const Vector& B) {return dcmp(A.x-B.x)==0 && dcmp(A.y-B.y)==0;}
inline double Cross(Vector A, Vector B)//叉积
{
return A.x * B.y - A.y * B.x;
}
int ConvexHull()
{
sort(p,p+n,myCmp);
int m = 0;
for(int i = 0; i <n; i++)
{
while(m > 1 && dcmp(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 && dcmp(Cross(ch[m-1]-ch[m-2], p[i]-ch[m-2])) <= 0) m--;
ch[m++] = p[i];
}
if(n > 1) m--;
return m;
}
double Dis(Point A, Point B)
{
return sqrt((A.x - B.x)*(A.x - B.x) + (A.y - B.y)*(A.y - B.y));
}
int main()
{
int m;
cin>>n;
for(int i = 0; i < n; i++)
{
int a,b;
cin>>a>>b;
p[i] = Point(a,b);
}
m = ConvexHull();
//计算凸包周长
double ans=0.0;
for(int i = 0; i < m-1; i++)
ans += Dis(ch[i],ch[i+1]);
ans += Dis(ch[m-1],ch[0]);
printf("%.1f",ans);
}
例题:
wikioi1298(click me)
求解二维凸包并计算周长
wikioi3201(click me)
求解二维凸包并计算周长
poj1113(click me)
本题翻译:http://blog.csdn.net/lytning/article/details/24046075
求解二维凸包并加入一个圆的周长