二维凸包是指覆盖平面上(n)个点的最小的凸多边形。
(Andrew)算法是一种求凸包的常用算法,它的时间复杂度是(O(nlogn))
算法流程
- 以x为第一关键字,y为第二关键字排序。
- 从1号点(最左下的一点)开始遍历,
- 若当前点在栈顶两个元素在直线的左边则入栈,
- 否则若在右边,说明有更优方案,栈顶的点出栈,直到在左边为止。
可以得到下凸包。
- 按相反方向重复第二步,得到上凸包。
二维向量的叉乘
想要判断两条直线的位置关系,需要用到向量的叉乘这一前置知识。
定义:设两个向量分别为(vec{a}=(x_1,y_1),vec{b}=(x_2,y_2)),
那么它们的叉乘即为(vec{a} imesvec{b}=x_1y_2-x_2y_1),它也是一个向量。
性质:(vec{a} imesvec{b}=-vec{b} imesvec{a})
几何意义:以两向量为邻边的平行四边形的有向面积;
定义向量(vec{a})、(vec{b}),
- 当(vec{a} imesvec{b}<0)时,(vec{b})在(vec{a})的顺时针方向;
- 当(vec{a} imesvec{b}=0)时,(vec{a})、(vec{b})共线(可以反向);
- 当(vec{a} imesvec{b}>0)时,(vec{b})在(vec{a})的逆时针方向。
因此,要判断点(z)是否在(x,y)所连直线的左边,可以判断是否有(vec{xy} imesvec{yz}>0)
代码如下
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#define MogeKo qwq
using namespace std;
const int maxn = 1e5+10;
int n,cnt;
double ans;
struct node {
double x,y;
bool operator < (const node & N) const {
if (x == N.x) return y < N.y;
return x < N.x;
}
} a[maxn],b[maxn];
double mul(node v1,node v2,node v3) {
double x_1,x_2,y_1,y_2;
x_1 = v2.x-v1.x, x_2 = v3.x-v2.x;
y_1 = v2.y-v1.y, y_2 = v3.y-v2.y;
return (x_1*y_2 - x_2*y_1);
}
double cal(node v1,node v2) {
double xx,yy;
xx = v1.x - v2.x;
yy = v1.y - v2.y;
return sqrt(xx*xx + yy*yy);
}
void solve(int op) {
cnt = 1;
b[1] = a[1];
for(int i = 2; i <= n; i++) {
if(op == 1) while(cnt > 1 && mul(b[cnt-1],b[cnt],a[i]) <= 0) cnt--;
if(op == 2) while(cnt > 1 && mul(b[cnt-1],b[cnt],a[i]) >= 0) cnt--;
b[++cnt] = a[i];
}
for(int i = 1; i < cnt; i++)
ans += cal(b[i],b[i+1]);
}
int main() {
scanf("%d",&n);
for(int i = 1; i <= n; i++)
scanf("%lf%lf",&a[i].x,&a[i].y);
sort(a+1,a+n+1);
solve(1);
solve(2);
printf("%.2lf
",ans);
return 0;
}