二维凸包
二维凸包是计算几何的基础算法。这里是Graham算法
我们首先找到一个一定在凸包上的点,即纵坐标最小的点中,横坐标也最小的点。
然后将其他的点按照与这个点的极角排序
用栈维护,依次扫描这些排序的点
然后如果当前点和栈顶的两个点形成了凸包,就将栈顶弹出。
加入当前点
对于三点共线的情况,我们将距离远的点排在距离近的点的后面,这样在弹的时候就会将近的点弹掉,只留下远的点。
没什么细节,注意eps
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#define eps 1e-6
using namespace std;
const int maxn=1e5;
struct point{
double x,y;
point(double a,double b){x=a,y=b;}
point(){x=0,y=0;}
point operator+(const point &b)const{return point(x+b.x,y+b.y);}
point operator-(const point &b)const{return point(x-b.x,y-b.y);}
point operator*(const double &b)const{return point(x*b,y*b);}
double operator*(const point &b)const{return x*b.y-y*b.x;}
double dot()const{return x*x+y*y;}
double dot(const point &b){return x*b.x+y*b.y;}
};
point p[maxn],q[maxn];
inline double cross(const point &a,const point &b){return a.x*b.y-a.y*b.x;}
double d(const point &a,const point &b){
// return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
return sqrt((a-b).dot());
}
int n,cnt;
double t,ans;
inline bool cmp(point a,point b){
double qaq=cross(a-p[1],b-p[1]);
if(qaq>eps) return true;
else if(fabs(qaq)<eps && d(b,p[1])-d(a,p[1])>eps) return true;
return false;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%lf %lf",&p[i].x,&p[i].y);
if(i>1 && (eps<p[1].y-p[i].y || (fabs(p[i].y-p[1].y)<eps && p[i].x-p[1].x<-eps))){
t=p[i].x;p[i].x=p[1].x;p[1].x=t;
t=p[i].y;p[i].y=p[1].y;p[1].y=t;
}
}
sort(p+2,p+1+n,cmp);
q[1]=p[1];cnt++;
for(int i=2;i<=n;i++){
while(cnt>1 && cross(q[cnt]-q[cnt-1],p[i]-q[cnt])<-eps) cnt--;
q[++cnt]=p[i];
}
q[++cnt]=p[1];
for(int i=2;i<=cnt;i++){
ans+=d(q[i],q[i-1]);
}
printf("%.2lf",ans);
return 0;
}