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

gate

二维凸包是指覆盖平面上(n)个点的最小的凸多边形。
(Andrew)算法是一种求凸包的常用算法,它的时间复杂度是(O(nlogn))

算法流程

  1. 以x为第一关键字,y为第二关键字排序。
  2. 从1号点(最左下的一点)开始遍历,
  • 若当前点在栈顶两个元素在直线的左边则入栈,
  • 否则若在右边,说明有更优方案,栈顶的点出栈,直到在左边为止。
    可以得到下凸包。
  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;
}
原文地址:https://www.cnblogs.com/mogeko/p/12564556.html