计算几何
最基础的东西
向量
高一的东西,不过多解释,可以理解为以 \((x,y)\) 表示一个有方向的线段
向量加减直接是 \(x,y\) 与另一个向量的 \(x,y\) 直接相加减,乘法有三种,与一个数相乘,直接 \(x,y\) 扩大为数字倍,向量间乘法都是得到一个数,分为两种,令两个向量为 \(a,b\)。
点乘:\(a.x*b.x+a.y*b.y\)。
叉乘:\(a.x*b.y-a.y*b.x\)。
叉乘的绝对值可以表示将这两个首首相接,尾尾用一条线段连起来形成的三角形的面积。
凸包
给你一堆点,然后让你选取一些点,连起来形成一个最小的,能包含所有点的多边形。
可以想象为桌上一堆钉子,最先开始一个橡皮筋耷拉着包围着所有钉子,你在水平方向拉着一个橡皮筋不断收缩,最后形成的那个多边形。
它用于求解的一个性质便是每一个内角不会大于180°(因为它是内角啊),而叉乘的正负恰能表示两向量在共起点时的旋转关系,所以通过叉乘就能判断出某次在考虑是否选点时是否会与之前的点形成凹进去的多边形。
然后发现我们可以按照一定顺序来找出这些点,例如从最左边的点开始,按照顺时针顺序,依次找出最终的点,然后发现在加入新点时,其最容易与最近选出的两点冲突,因为最终每一条边的斜率是不断递减的,所以越近的边就越容易形成不满足性质的角,所以用栈来存储一次选择的点,每次判断是否冲突即可。但我们发现在枚举点时,我们不能真的做到按顺时针顺序,只能比如按照横坐标来排序,在这种情况下,我们一次从头到尾只能求出凸包上面的那一部分,所以还要再从右往左把凸包下面的边一条条连好。
感觉还是有点混乱,后面还需多理解一下。
例题
一道给点求凸包周长的题。
#include<bits/stdc++.h>
using namespace std;
double eps=1e-12;
double PI=acos(-1);
int n,cnt;
struct node{
double x,y;
}cs[100005];
bool operator < (const node &a,const node &b){
return a.x==b.x?a.y<b.y:a.x<b.x;
}
node operator + (node a,node b){
return (node){a.x+b.x,a.y+b.y};
}
node operator - (node a,node b){
return (node){a.x-b.x,a.y-b.y};
}
node operator * (node a,double b){
return (node){a.x*b,a.y*b};
}
double dot(node a,node b){
return a.x*b.x+a.y*b.y;
}
double cross(node a,node b){
return a.x*b.y-a.y*b.x;
}
node rotate(node a,double b){
double c=cos(b),s=sin(b);
return (node){a.x*c-a.y*s,a.y*c+a.x*s};
}
double len(node a){
return sqrt(a.x*a.x+a.y*a.y);
}
int sta[100005],tp;
double a,b,r,x,y,t;
int main()
{
scanf("%d",&n);
cnt=n;
//scanf("%lf",&r);
//a/=2.0,b/=2.0;
for(int i=1;i<=n;i++){
scanf("%lf%lf",&x,&y);
cs[i]=(node){x,y};
}
sort(cs+1,cs+cnt+1);
for(int i=1;i<=cnt;i++){
while(tp>1&&cross(cs[sta[tp]]-cs[sta[tp-1]],cs[i]-cs[sta[tp]])<=0)tp--;
sta[++tp]=i;
}
int sp=tp;
for(int i=cnt-1;i>=1;i--){
while(tp>sp&&cross(cs[sta[tp]]-cs[sta[tp-1]],cs[i]-cs[sta[tp]])<=0)tp--;
sta[++tp]=i;
}
double ans=0;
for(int i=2;i<=tp;i++){
ans+=len(cs[sta[i]]-cs[sta[i-1]]);
}
printf("%.2lf",ans);
return 0;
}