题解
二维凸包模板题。
使用简单迅速的 Graham Scan 算法可以直接求出整个凸包。算法流程:
- 选出所有点中 y 坐标最小的点,作为原点,建立极坐标系。
- 将其余点以极角排序,极角相同看长度
- 将基准点与第一点入栈,依次扫描之后的点,设栈顶点为 \(b\),栈顶之下一个点为 \(a\),当前扫描到的点是 \(c\),如果 \(\vec{ab}\) 转向 \(\vec{ac}\) 为顺时针,那么将 \(b\) pop出栈。判断位置使用向量叉积实现,如果顺时针,那么 \(\vec{ab}\times\vec{ac}<0\)
- 扫描完所有点之后,栈里面的所有点构成凸包。
代码
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#define xx first
#define yy second
using namespace std;
typedef long long LL;
typedef pair<double,double> PDD;
const int N=3e5+10;
const double PI=acos(-1.0);
int n,top;
PDD p[N],sta[N];
double ans;
double X(PDD a,PDD b,PDD c){
return (b.xx-a.xx)*(c.yy-a.yy)-(c.xx-a.xx)*(b.yy-a.yy);
}
double len(PDD a,PDD b){
return sqrt((a.xx-b.xx)*(a.xx-b.xx)+(a.yy-b.yy)*(a.yy-b.yy));
}
bool cmp(PDD a,PDD b){
double pp=X(p[1],a,b);
if(pp>0) return true;
if(pp<0) return false;
return len(p[1],a)<len(p[1],b);
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%lf%lf",&p[i].xx,&p[i].yy);
if(n==1) return printf("0.00\n"),0;
else if(n==2) return printf("%.2lf\n",len(p[1],p[2])),0;
for(int i=1;i<=n;i++)
if(p[i].yy<p[1].yy||p[i].yy==p[1].yy&&p[i].xx<p[1].xx)
swap(p[i],p[1]);
sort(p+2,p+n+1,cmp);
sta[++top]=p[1];sta[++top]=p[2];
for(int i=3;i<=n;i++){
while(top>1&&X(sta[top-1],sta[top],p[i])<=0) top--;
sta[++top]=p[i];
}
for(int i=1;i<top;i++) ans+=len(sta[i],sta[i+1]);
ans+=len(sta[top],p[1]);
printf("%.2lf\n",ans);
return 0;
}