所谓凸包,就是一个凸出来的包
(逃)
前言
计算集合的第一课。
关键特征:周长最小。此时一定是凸包。
解析
定义
凸包:在平面上能包含所有给定点的最小凸多边形叫做凸包。
性质:凸包的周长是所有能包含给定点的多边形中最小的。
维护
凸包的主流求法有 Andrew 和 Graham,两者的复杂度瓶颈都在于排序,为 O(nlogn)O(n\log n)O(nlogn)。
这里介绍较为简单的 Graham 扫描法。
首先,找出所有点中按照 X−YX-YX−Y 排序最小的点 OOO。
然后以 OOO 作为原点,把所有其它点按照极角排序,极角相同时按距离升序排序(实测这里也可以降序,但是绝对不能无序)。
可以用快排重载 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_CLOCKWISEdouble 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);
#endifn=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
*/