模板:二维凸包(计算几何)

所谓凸包,就是一个凸出来的包

(逃)

前言

计算集合的第一课。
关键特征:周长最小。此时一定是凸包。

解析

定义

凸包:在平面上能包含所有给定点的最小凸多边形叫做凸包。

性质:凸包的周长是所有能包含给定点的多边形中最小的。

维护

凸包的主流求法有 Andrew 和 Graham,两者的复杂度瓶颈都在于排序,为 O ( n log ⁡ n ) O(n\log n) O(nlogn)
这里介绍较为简单的 Graham 扫描法。

首先,找出所有点中按照 X − Y X-Y XY 排序最小的点 O O O
然后以 O O O 作为原点,把所有其它点按照极角排序,极角相同时按距离升序排序(实测这里也可以降序,但是绝对不能无序)。
可以用快排重载 cmp 函数的方法实现,利用叉积判断。

bool cmp(V a,V b){
	double d=(a-p[1])^(b-p[1]);
	if(abs(d)>eps) return d>0;
	else return len(a-p[1])<len(b-p[1]);
}

排好序后,开一个栈维护当前凸包中的点。
如果新点破坏了凸性,则不断退栈。
最终栈内的元素就是凸包中的点。

是否破坏凸性可以用叉积判断,如果新点和栈顶形成的向量向右拐了(叉积小于0),则说明破坏了凸性。

注意:这里判断退栈的条件最好带等!,一方面,共线时在边上的顶点没有什么意义,另一方面,当有重合点时,不带等会导致程序错误。

void ConvexHull(V *p,int &n){
	int top=0;
	sort(p+1,p+1+n);
	sort(p+2,p+1+n,cmp);
	top=0;
	for(int i=1;i<=n;i++){
		while((top>1&&((zhan[top]-zhan[top-1])^(p[i]-zhan[top]))<eps)) --top;
		zhan[++top]=p[i];
	}
	memcpy(p,zhan,sizeof(zhan));
	n=top;
	return;
}

完整代码

P2742 [USACO5.1]圈奶牛Fencing the Cows /【模板】二维凸包

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
inline ll read(){
  ll x(0),f(1);char c=getchar();
  while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
  while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
  return x*f;
}

//basic declare
//#define double long double
const double eps=1e-10;
const double pi=acos(-1.0);

//---about vectors (or points)

//definition
struct V{
  double x,y;
  V():x(0),y(0){}
  V(double a,double b):x(a),y(b){}
};
V ans[10];//declared for other functions
int tot;
inline void input(V &a){scanf("%lf%lf",&a.x,&a.y);}
void print(const V &a,int op=1){printf("%.10lf %.10lf",a.x,a.y);putchar(op?10:32);}
//op:endl or space

//basic operation
inline V operator + (const V &a,const V &b){return (V){a.x+b.x,a.y+b.y};}
inline V operator - (const V &a,const V &b){return (V){a.x-b.x,a.y-b.y};}
inline V operator * (const double &x,const V &a){return (V){a.x*x,a.y*x};}
inline V operator * (const V &a,const double &x){return (V){a.x*x,a.y*x};}
inline V operator / (const V &a,const double x){return (V){a.x/x,a.y/x};}
inline bool operator == (const V &a,const V &b){return abs(a.x-b.x)<eps&&abs(a.y-b.y)<eps;}
inline bool operator != (const V &a,const V &b){return !(a==b);}
inline double operator * (const V &a,const V &b){return a.x*b.x+a.y*b.y;}
inline double operator ^ (const V &a,const V &b){return a.x*b.y-a.y*b.x;}
inline double len(const V &a){return sqrt(a.x*a.x+a.y*a.y);}
inline V mid(const V &a,const V &b){return (V){(a.x+b.x)/2,(a.y+b.y)/2};}
inline V chui(const V &a){return (V){a.y,-a.x};}//not take direction into account
inline V danwei(const V &a){return a/len(a);}
inline double tri_S(const V &a,const V &b,const V &c){return abs((b-a)^(c-a))/2;}//always be non-negative
inline bool operator < (const V &a,const V &b){
  return a.x<b.x-eps||(abs(a.x-b.x)<eps&&a.y<b.y-eps);
}
inline double ang(const V &a,const V &b){return acos((a*b)/len(a)/len(b));}
inline V rotate(const V &o,double t){//COUNTER_CLOCKWISE
  double s=sin(t),c=cos(t);
  return (V){o.x*c-o.y*s,o.x*s+o.y*c};
}

const int N=1e5+100;
const int M=505;
int n,m;
V p[N],zhan[N];
bool cmp(V a,V b){
	double d=(a-p[1])^(b-p[1]);
	if(abs(d)>eps) return d>0;
	else return len(a-p[1])<len(b-p[1]);
}
void ConvexHull(V *p,int &n){
	int top=0;
	sort(p+1,p+1+n);
	sort(p+2,p+1+n,cmp);
	top=0;
	for(int i=1;i<=n;i++){
		while((top>1&&((zhan[top]-zhan[top-1])^(p[i]-zhan[top]))<eps)) --top;
		zhan[++top]=p[i];
	}
	memcpy(p,zhan,sizeof(zhan));
	n=top;
	return;
}
signed main(){
#ifndef ONLINE_JUDGE
  //freopen("a.in","r",stdin);
  //freopen("a.out","w",stdout);
#endif
  n=read();
  for(int i=1;i<=n;i++) input(p[i]);
  ConvexHull(p,n);
  zhan[n+1]=p[1];
  double res(0);
  for(int i=1;i<=n;i++) res+=len(zhan[i+1]-zhan[i]);//print(zhan[i]);
  printf("%.2lf\n",res);
  return 0;
}
/*
3 5
0 -2
-5 3
0 -7
*/



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值