凸包学习笔记
例题: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\n",ans); return 0; }