计算几何之凸包

今天我们要涉及计算几何中的凸包。凸包(Convex Hull)是一个计算几何(图形学)中的概念。
凸包这个东西,我们其实并不陌生,因为在斜率优化的时候我们就需要用到凸包来进行辅助。但是今天讲的是怎么求出凸包。
在一个实数向量空间V中,对于给定集合X,所有包含X的凸集的交集S被称为X的凸包。X的凸包可以用X内所有点(X1…Xn)的凸组合来构造.
在二维欧几里得空间中,凸包可想象为一条刚好包著所有点的橡皮圈。
那么首先给出凸包的正式定义:
对于一个集合D,所有包含D的凸集之交称为D的凸包。
用不严谨的话来讲,给定二维平面上的点集,凸包就是将最外层的点连接起来构成的凸多边形,它能包含点集中所有的点。
那么我们怎么求凸包呢?首先有的就是暴力穷举的想法。我们今天介绍的是一种更为优秀的方法:graham扫描法。
在知道这种方法前,我们需要知道一个概念:向量的叉积。

向量的叉积
向量(矢量)的叉积在数学,物理(又名叉乘),信息学中都有重要的应用。比如物理里面的力矩,就等于F矢量叉乘上r矢量。矢量叉积的表达式为 A B c o s ( φ ) ,方向用右手螺旋定则判定。而如果我们将两个向量对应到平面极座标上,那么若向量A为点(a,b),向量B为点(c,d),那么A和B的叉积为 ( a d b c ) 。我们发现当向量叉积为正时,向量A的幅角小于向量B的幅角。于是我们可以用向量的叉积判断两个向量的角度关系。

关于精度问题
由于double类型精度上还是存在一点问题,所以我们在a==b时要写成fabs(a-b)<eps,eps一般取1e-71e-10左右
< , > , <= , >= 同理。

Graham扫描法
首先我们可以立即发现凸包一个性质:所有点中最左下角的点一定是凸包上的点。那么我们就按所有点与最左下角的点(后文称为 P 0 )构成的向量的幅角的角度排序,如下图:

这里写图片描述
这里写图片描述

然后按照排序顺序根据相邻两边的拐向开始贪心。由于凸包的性质,每次拐都不能使叉积小于0。
具体实现可以利用一个栈存下当前的凸包上的点,每次取栈顶两个点和新的点进行拐向判断就行了。

这里写图片描述
CODE:

#include<bits/stdc++.h>
#define db double
#define MAXN 100005
using namespace std;
const db eps=1e-8;
struct node{
    db x,y;
    node (db xx=0,db yy=0){x=xx,y=yy;}
}F[MAXN];
node operator+(node a,node b){return node(a.x+b.x,a.y+b.y);}
node operator-(node a,node b){return node(a.x-b.x,a.y-b.y);}
db operator*(node a,node b){return a.x*b.y-a.y*b.x;}
double dis(node x,node y){
    return sqrt((y.y-x.y)*(y.y-x.y)+(y.x-x.x)*(y.x-x.x));
}
int cmp(node a,node b){
    db tmp=(a-F[1])*(b-F[1]);
    if(tmp>eps) return 1;
    if(fabs(tmp)<eps&&dis(a,F[1])-dis(b,F[1])<eps) return 1;
    return 0;
}
int n,sta[MAXN],top;
db ans;
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%lf%lf",&F[i].x,&F[i].y);
        if(F[i].y-F[1].y<-eps||fabs(F[i].y-F[1].y)<eps&&F[i].x-F[1].x<eps) swap(F[1],F[i]);
    }
    sort(F+2,F+1+n,cmp);
    sta[++top]=1;sta[++top]=2;
    for(int i=3;i<=n;i++){
        while(top>1&&(F[sta[top]]-F[sta[top-1]])*(F[i]-F[sta[top-1]])<eps) top--;
        sta[++top]=i;
    }
    for(int i=2;i<=top;i++) ans+=dis(F[sta[i-1]],F[sta[i]]);   //统计所有凸包上的点的距离
    ans+=dis(F[sta[top]],F[1]);
    printf("%.2lf",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/stevensonson/article/details/80720590
今日推荐