【理论】二维凸包求法
本文仅介绍 Andrew 算法。
考虑一个凸包的本质是什么?是由一个上凸壳和下凸壳组合形成的多边形
你说的对,但是上凸壳和下凸壳的本质又是什么?
是一堆斜率单调的直线。
凸包求法似乎呼之欲出了。
我们要采取增量法,所以我们需要有一个加入点并动态维护之的顺序,我们先将所有点按 \(x\) 坐标排序。
紧接着,以下凸壳为例,我们维护一个栈(某种意义上,可以看做单调栈),栈内放着我们目前维护的下凸壳内的所有点,按 \(x\) 坐标的顺序每次加入一个新点作为增量,如何判定该点对下凸壳的影响?将栈顶和该点分别与栈顶下第一点连线,比较两者斜率的大小,若前者更大(建议画图理解),则为了保持斜率的单调性(该点作为增量必须加入),需要将栈顶弹出,如此操作直到栈顶无需弹出或栈的大小为 \(1\) 时,将该点入栈。
上凸壳亦然,不过加入新点作为增量要按照 \(x\) 坐标的逆序进行。
最终凸包\(=\)上凸壳\(\cup\)下凸壳。
代码
//Andrew
ll n,s[100005],top;
double ans;
int main(){
rd(n);
rep(i,1,n) cin>>a[i].x>>a[i].y;
sort(a+1,a+n+1,cmp);
s[top=1]=1;
rep(i,2,n){
do{
if(top<2) break;
if(slope(s[top-1],s[top])>slope(s[top-1],i)) --top;
else break;
}while(1);
s[++top]=i;
}
while(top>1){
ans+=dis(s[top],s[top-1]);
--top;
}
s[top=1]=n;
repp(i,n-1,1){
do{
if(top<2) break;
if(slope(s[top-1],s[top])>slope(s[top-1],i)) --top;
else break;
}while(1);
s[++top]=i;
}
while(top>1){
ans+=dis(s[top],s[top-1]);
--top;
}
printf("%.2lf",ans);
}