P2742 [USACO5.1]圈奶牛Fencing the Cows /【模板】二维凸包
时间:2020-03-25 11:19:59
收藏:0
阅读:55
二维凸包是指覆盖平面上\(n\)个点的最小的凸多边形。
\(Andrew\)算法是一种求凸包的常用算法,它的时间复杂度是\(O(nlogn)\)
算法流程
- 以x为第一关键字,y为第二关键字排序。
- 从1号点(最左下的一点)开始遍历,
- 若当前点在栈顶两个元素在直线的左边则入栈,
- 否则若在右边,说明有更优方案,栈顶的点出栈,直到在左边为止。
可以得到下凸包。
- 按相反方向重复第二步,得到上凸包。
二维向量的叉乘
想要判断两条直线的位置关系,需要用到向量的叉乘这一前置知识。
定义:设两个向量分别为\(\vec{a}=(x_1,y_1),\vec{b}=(x_2,y_2)\),
那么它们的叉乘即为\(\vec{a}\times\vec{b}=x_1y_2-x_2y_1\),它也是一个向量。
性质:\(\vec{a}\times\vec{b}=-\vec{b}\times\vec{a}\)
几何意义:以两向量为邻边的平行四边形的有向面积;
定义向量\(\vec{a}\)、\(\vec{b}\),
- 当\(\vec{a}\times\vec{b}<0\)时,\(\vec{b}\)在\(\vec{a}\)的顺时针方向;
- 当\(\vec{a}\times\vec{b}=0\)时,\(\vec{a}\)、\(\vec{b}\)共线(可以反向);
- 当\(\vec{a}\times\vec{b}>0\)时,\(\vec{b}\)在\(\vec{a}\)的逆时针方向。
因此,要判断点\(z\)是否在\(x,y\)所连直线的左边,可以判断是否有\(\vec{xy}\times\vec{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\n",ans);
return 0;
}
原文:https://www.cnblogs.com/mogeko/p/12564556.html
评论(0)