二维凸包

二维凸包是计算几何的基础算法。这里是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;
}
posted on 2019-12-27 21:49  萌德真帅  阅读(172)  评论(0编辑  收藏  举报