凸包
已知一个点集,凸包就是指将这些些点全部包围在内部的,且面积最小的凸多边形。
举个例子
以下介绍一种解决凸包问题的算法Andrew算法。
Andrew算法
首先先把所有点排序(排序方法同前面定义<相同),必要时删除重复点,然后把点1和点2放入凸包的栈中,然后从p3开始当新的点在凸包前进的方向的左边时加入并继续,否则说明方向已经向内凹了,此时依次删除当时在栈顶的点,直至新点在左边。
依旧举栗子:
此时新点E方向在向量CD的右边,所以需要在凸包的栈中删除C和D,让B的下一个点为E,重复此过程,直至碰到最右边的pn,求出了凸包下部分,然后反过来求出凸包的上部分,合起来求得完整的凸包。
Andrew算法复杂度并不高,排序后处理就是线性的O(n),加上排序也只是O(nlogn)。
代码:
void Andrew()
{
sort(a+1,a+n+1);
tot=0;
for (int i=1;i<=n;i++)
{
while (tot>1&&dcmp(cross(b[tot]-b[tot-1],a[i]-b[tot-1]))<0) tot--;
b[++tot]=a[i];
}
int k=tot;
for (int i=n-1;i>0;i--)
{
while (tot>k&&dcmp(cross(b[tot]-b[tot-1],a[i]-b[tot-1]))<0) tot--;
b[++tot]=a[i];
}
if (n>1) tot--;//由于第一个点会算两遍,所以最终凸包点个数要-1,但有一些求凸包周长的时候为了计算方便去掉这一行。
}
模板:POJ 1113 说是凸包的边长加2*pi*r(r已给出),然而菜鸡fhj不会证明,若各位大佬知道请务必在评论区教教本菜鸡。
#include<cmath>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,tot;
double ans,R,eps=1e-5;
struct data
{
double x,y;
data (double x=0,double y=0):x(x),y(y){}
bool operator < (const data b) const{
return (x<b.x||(x==b.x&&y<b.y));
}
}a[1005],b[1005];
data operator - (const data a,const data b){return data(a.x-b.x,a.y-b.y);}
double dot(const data a,const data b){return a.x*b.x+a.y*b.y;}
double leng(const data x){return sqrt(dot(x,x));}
double cross(const data a,const data b){return a.x*b.y-a.y*b.x;}
int dcmp(double x)
{
if (fabs(x)<eps) return 0;
return (x<0)?-1:1;
}
void Andrew()
{
sort(a+1,a+n+1);
tot=0;
for (int i=1;i<=n;i++)
{
while (tot>1&&dcmp(cross(b[tot]-b[tot-1],a[i]-b[tot-1]))<0) tot--;
b[++tot]=a[i];
}
int k=tot;
for (int i=n-1;i>0;i--)
{
while (tot>k&&dcmp(cross(b[tot]-b[tot-1],a[i]-b[tot-1]))<0) tot--;
b[++tot]=a[i];
}
}
int main()
{
freopen("wall.in","r",stdin);
freopen("wall.out","w",stdout);
scanf("%d%lf",&n,&R);
for (int i=1;i<=n;i++)
{
double x,y; scanf("%lf%lf",&x,&y); a[i]=data(x,y);
}
Andrew(); ans=0.0;
for (int i=1;i<tot;i++) ans+=leng(b[i+1]-b[i]);
printf("%0.0lf",ans+2*R*3.14159);
return 0;
}
PS:本博客比较简陋,加之作者fhj水平实在菜鸡,所以各位大佬请多多谅解,如有错误,请多多指出!
PPS:Orz zzk