凸包学习笔记

例题:P2742 [USACO5.1]圈奶牛Fencing the Cows /【模板】二维凸包

https://www.luogu.com.cn/problem/P2742

之前一直没有学习过计算几何的知识,也没有遇到相关的题。但是最近发现这类型的题多了起来,于是决定学习一下计算几何的知识。

以这道模板题为例,我们可以以y坐标最小的点为原点,对所有点进行极角排序。

我用的极角排序的方法是比较与原点连线的斜率大小。

然后按斜率从小到大枚举,每次遇到一个点,判断他是否可以覆盖之前的点,如果可以就弹栈,然后将这个点加入栈。

因为已经按斜率排过序,所以如果一个点不能将上一个点弹出栈,也就不能将再上一个点弹出栈。

最后再将原点加入栈,统计答案即可。

还有一种简单的方法,使用atan2函数,在另一篇博客会有介绍。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define db double
#define N 200000
using namespace std;
int n,num,i,mnk;
db ans,mnx,mny;
struct node{
    db x,y;
}f[N],data[N];
db dis(node x,node y){
    return sqrt((x.x-y.x)*(x.x-y.x)+(x.y-y.y)*(x.y-y.y));
}
int check(node x1,node x2,node y1,node y2){
    if ((x2.x-x1.x)*(y2.y-y1.y)>(y2.x-y1.x)*(x2.y-x1.y))
        return 1;
    if ((x2.x-x1.x)*(y2.y-y1.y)==(y2.x-y1.x)*(x2.y-x1.y))
        return 0;
    return -1;
}
int cmp(node x,node y){
    int temp=check(f[n],x,f[n],y);
    if (temp==1||temp==0&&dis(x,f[n])<dis(y,f[n]))
        return 1;
    return 0;
}
void swap1(node &x,node &y){
    node k;
    k=x;
    x=y;
    y=k;
}
int main(){
    scanf("%d",&n);
    mny=mnx=1e9;
    for (i=1;i<=n;i++){
        scanf("%lf%lf",&f[i].x,&f[i].y);
        if (f[i].y<mny){
            mnk=i;
            mnx=f[i].x;
            mny=f[i].y;
        }
        if (f[i].y==mny&&f[i].x<mnx){
            mnk=i;
            mnx=f[i].x;
            mny=f[i].y;
        }
    }
    swap(f[n],f[mnk]);
    sort(f+1,f+n,cmp);
    num=1;
    data[1]=f[n];
    for (i=1;i<n;i++){
        while (num>1&&check(data[num-1],data[num],data[num],f[i])<=0)
            num--;
        num++;
        data[num]=f[i];
    }
    data[++num]=f[n];
    for (i=2;i<=num;i++)
        ans+=dis(data[i],data[i-1]);
    printf("%.2lf
",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/Mohogany/p/13727008.html