凸包与Andrew算法——学习笔记

凸包

已知一个点集,凸包就是指将这些些点全部包围在内部的,且面积最小的凸多边形。
举个例子
这里写图片描述

以下介绍一种解决凸包问题的算法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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值