计算几何
前言
- 数学统治直世界!
向量
- 如果你会高中向量,请直接跳过到叉乘
定义
- 向量既有大小又有方向,和物理的矢量很像,手写一般用 \(\overrightarrow{a}\) 表示,印刷用黑体 \(\textbf{a}\) 表示,为了好看,我们采用手写方式
- 以下有一些关于向量的概念和特殊向量的定义
模
- 就是向量长度,\(\overrightarrow{a}\) 的模用 \(\mid \overrightarrow{a} \mid\) 表示
共线向量
- 两向量所在直线重合或者平行
垂直向量
- 两向量所在直线垂直
相反向量
- 两向量大小相等,方向相反
单位向量
- 模长为 \(1\) 的向量
零向量
- 模为零的向量,方向无法确定
方向向量
- 直线平行的任意非零向量,用来表示这条直线的方向
运算
加减
加法
- 将向量首尾相接
- \(e.g.\)
\[\overrightarrow{OA} + \overrightarrow{AB} = \overrightarrow{OB}
\]
减法
- 减法相当于是一个向量加一个向量的反向量
- \(e.g.\)
\[\overrightarrow{OA} - \overrightarrow{OB} = \overrightarrow{OA} + \overrightarrow{BO} = \overrightarrow{BA}
\]
数乘
- 数乘是向量乘数,直接乘
\[\overrightarrow{a} \times \lambda = \lambda\overrightarrow{a}
\]
点乘
- 点乘又称数量积,是向量乘向量,用 \(\overrightarrow{a} \cdot \overrightarrow{c}\) 表示(\(a,b\) 不一样高太丑了所以用 \(a,c\)),结果是数,几何意义是一个向量在另一个向量上的投影长
- 点乘运算如下
\[\overrightarrow{a} \cdot \overrightarrow{c} = \mid \overrightarrow{a} \mid \times \mid \overrightarrow{c} \mid \times \cos \left\langle \> \overrightarrow{a},\overrightarrow{c} \> \right\rangle
\]
- 其中,\(\cos \left\langle \> \overrightarrow{a},\overrightarrow{c} \> \right\rangle\) 表示 \(\overrightarrow{a}\) 和 \(\overrightarrow{c}\) 的夹角的 \(\cos\) 值
叉乘
-
叉乘又称叉积,外积,用 \(\overrightarrow{a} \times \overrightarrow{c}\),结果是向量,几何意义是两向量由平行四边形法则围成的面积(不知道平行四边形法则的可以有求 \(bing\))
-
叉乘有正负,具体判法依据右手定则,形象化地,有两个向量 \(\overrightarrow{a}\) 和 \(\overrightarrow{c}\),右手四指从 \(\overrightarrow{a}\) 转向 \(\overrightarrow{c}\) 且转角 \(<\pi\) 时,大拇指方向就是叉乘结果的方向,若大拇指向上就是正,反之为负
-
\(e.g.\)

- 其中 \(\overrightarrow{a} \times \overrightarrow{b} > 0\)
- 叉乘运算如下
\[\overrightarrow{a} \times \overrightarrow{c} = \mid \overrightarrow{a} \mid \times \mid \overrightarrow{c} \mid \times \sin \left\langle \> \overrightarrow{a},\overrightarrow{c} \> \right\rangle
\]
向量的坐标表示
- 向量是可以用坐标表示的,如图

- \(\overrightarrow{a}\) 向量就被表示成 \((x,y)\),\(\mid \overrightarrow{a} \mid = \sqrt {x^2 + y^2}\)
运算
- 在坐标意义下,运算就很容易了
加减法
- \(\overrightarrow{a} = (x_1,y_1),\overrightarrow{c} = (x_2,y_2)\),那么
\[\overrightarrow{a} + \overrightarrow{c} = (x_1+x_2,y_1+y_2)
\]
\[\overrightarrow{a} - \overrightarrow{c} = (x_1-x_2,y_1-y_2)
\]
数乘
- \(\overrightarrow{a} = (x_1,y_1),\lambda \in R\),那么
\[\overrightarrow{a} \times \lambda = (\lambda x_1,\lambda y_1)
\]
点乘
- \(\overrightarrow{a} = (x_1,y_1),\overrightarrow{c} = (x_2,y_2)\),那么
\[\overrightarrow{a} \cdot \overrightarrow{c} = x_1x_2 + y_1y_2
\]
叉乘
- \(\overrightarrow{a} = (x_1,y_1),\overrightarrow{c} = (x_2,y_2)\),那么
\[\overrightarrow{a} \times \overrightarrow{c} = x_1y_2−x_2y_1
\]
- 如果你会立体几何,那我们可以用叉乘求平面法向量,因为叉乘的结果垂直于两向量
- \(\overrightarrow{a} = (a_x,a_y,a_z),\overrightarrow{c} = (c_x,c_y,c_z)\),那么这两点的叉乘结果为:
\[(a_yc_z − a_zc_y,a_zc_x − a_xc_z,a_xc_y − a_yc_x)
\]
-
那你直接有法向量
-
向量到这了,呼呼呼
交点,距离,位置
点与直线的位置关系
- 已知一条直线的方向向量 \(\overrightarrow{a}\) 和一个在这条直线上的点 \(m\) 以及一个直线外的点 \(n\),求 \(n\) 在直线的左侧还是右侧
- 这个挺好做,将 \(\overrightarrow{a} \times \overrightarrow{mn}\) 然后判正负就行
两条线段位置关系
- 平行或重合好判,不说了,关键是是否相交
快速排斥实验与跨立实验
- 这俩可以方便判断两条线段是否相交
快速排斥实验
- 每一条线段都有一个“影响范围”,如下

- 这俩线段的“影响范围”没有相交,所以这俩线不交,这就是快速排斥实验,不过可能判不全,比如:

跨立实验
- 我们知道一条线段与另一条线段相交,一条线左右端点会居于另一条线的两侧,我们依据此进行判断,这就是跨立实验,注意:两条直线的端点都必须位于另一条线的两侧,同样,这个也会判不全,所以我们需要将快速排斥实验与跨立实验结合用
点与多边形关系
光线投射算法
- 说白了就是从点引射线,与多边形交偶数个点就在外部,奇数个就在内部

- 不过对于边自交的多边形并不适用
- 还有一个回转数算法,不会。。。
两直线交点坐标
- 你可以构建一次函数用解析几何的方法直接解方程,不过我们是基于线性代数的,和解析几何不属于一个区域,可能在扩展性上有一些问题
- 我们构建三角形,通过正弦定理求解

\[T=\frac{\mid \overrightarrow{u} \times \overrightarrow{c}\mid}{\mid\overrightarrow{a}\times \overrightarrow{c}\mid}=\frac{\mid\overrightarrow{u}\mid\sin \theta}{\mid\overrightarrow{a}\mid\sin \phi}
\]
- 然后,我们用 \(C\) 点减去 \(T \>\overrightarrow{a}\) 就是坐标
直线与圆
- 你仍然可以用解析几何做
- 若有两交点,求出两交点的中点,加半个弦长

圆与圆
解析几何强强?!- 将一圆圆心与交点相连,求出两圆心连线与该连线所成角,得到方向向量,沿方向向量方向将圆心平移半径长度即可得到一个交点,另一个同理

距离
- 我们先介绍一下距离
欧几里得距离
- 就是我们学的,两点 \((x_1,y_1),(x_2,y_2)\),距离为 \(\sqrt {(x_1-x_2)^2+(y_1-y_2)^2}\)
曼哈顿距离
- 两点 \((x_1,y_1),(x_2,y_2)\),距离为 $\mid x_1 - x_2 \mid + \mid y_1 - y_2 \mid $
- 给张图

- 曼哈顿距离可以有效保证距离是整数,并且经过任何点的 \(x\) 到 \(y\) 曼哈顿距离都不小于直接 \(x\) 到 \(y\)
切比雪夫距离
- 两点 \((x_1,y_1),(x_2,y_2)\),距离为 \(\max{(\mid x_1 - x_2 \mid , \mid y_1 - y_2 \mid )}\)
- 还是画图

- 曼哈顿距离和切比雪夫距离是可以互相转化的
- 我们先构建平面直角坐标系,找到曼哈顿距离和切比雪夫距离分别为 \(1\) 的点

- 你发现其有对应关系
- \((x,y) \to (x + y, x - y)\),\(Manhattan \to Чебышев\)
- \((x,y) \to (\dfrac{x + y}{2},\dfrac{x - y}{2})\),\(Чебышев \to Manhattan\)
闵可夫斯基距离
- 这个没听过,好像只有在闵和见过,以后学闵和再说,先搞定义
- 对于一个 \(n\) 维的两点 \((x_1,x_2,\dots,x_n),(y-1,y_2,\dots,y_n)\),有距离:
\[\left(\sum_{i=1}^n \left\vert x_i - y_i \right\vert ^p\right)^{\frac{1}{p}}
\]
- 当 \(p=1\) 时为曼哈顿距离
- 当 \(p=2\) 时为欧几里得距离
- 当 \(p \to \infty\) 时为切比雪夫距离
面积
多边形面积
- 将多边形 \(p_1,p_2,\dots,p_n\) 分成若干个三角形,称为三角剖分,选择一个点 \(O\),并令 \(v_i =p_i − 𝑂\),有:
\[S= \frac{1}{2}\left| \sum_{i=1}^n \overrightarrow{v_i} \times \overrightarrow {v_{i+1(\bmod n)}} \right|
\]
凸包
- 进行一个凸包的讲
凸多边形
- 所有内角大小都在 \([0,\pi]\) 的简单多边形
二维凸包
- 我们形象化地说,就是在一个木板上钉钉子,然后用橡皮筋套上,就是凸包,画图

- 里面的点不在边上
- 换种说法,就是找到周长最小的多边形使所有点都被包含
- 求法有两种,我们先口胡
\(Andrew\)
- \(Andrew\) 算法实现依据单调栈,分开处理上下凸包,为啥等会说,具体做法是:
- 按照 \(x\) 为第一关键字,\(y\) 为第二关键字排序,最小的元素和最大的元素一定在凸包上
- 下半部分,我们要让斜率尽可能地小,通过单调栈维护两条线段的斜率问题
- 上半同理
- 你发现会有一些点是要求斜率小,一些要求大,所以我们分开处理,再统计点的个数
点击查看代码
#include<bits/stdc++.h>
#define ld long double
#define pdd pair<ld,ld>
#define fi first
#define se second
using namespace std;
constexpr int maxn=1e5+10;
int n;
pdd a[maxn];
pdd stk[maxn];
int top;
int chk(int top,int i)
{
return
(a[i].se-stk[top].se)*
(stk[top].fi-stk[top-1].fi)>=
(stk[top].se-stk[top-1].se)
*(a[i].fi-stk[top].fi);
}
ld dou(ld x) {return x*x;}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
scanf("%Lf%Lf",&a[i].fi,&a[i].se);
sort(a+1,a+1+n,[](pdd x,pdd y) {return x.fi == y.fi ? x.se<y.se : x.fi<y.fi;});
stk[++top]=a[1];
for (int i=2;i<=n;i++)
{
while (top>1 && chk(top,i)) top--;
stk[++top]=a[i];
}
ld ans=0.0;
for (int i=2;i<=top;i++)
ans+=sqrt(dou(stk[i].fi-stk[i-1].fi)+dou(stk[i].se-stk[i-1].se));
stk[top=1]=a[n];
for (int i=n-1;i;i--)
{
while (top>1 && chk(top,i)) top--;
stk[++top]=a[i];
}
for (int i=2;i<=top;i++)
ans+=sqrt(dou(stk[i].fi-stk[i-1].fi)+dou(stk[i].se-stk[i-1].se));
printf("%.2Lf\n",ans);
return 0;
}
\(Graham\)
- \(Graham\) 算法和 \(Andrew\) 算法相似
- 选择纵坐标最小的点,这个点一定在凸包上
- 求其他点相对于这个点的极角排序
- 按照极角序依次尝试加入每一个点到单调栈里
- 假设连续选三个点 \(A,B,C\),有:\(\overrightarrow{AB} \times \overrightarrow{BC} \ge 0\),单调栈排除不合法点
三维凸包
- 其实和二维没啥区别,三维凸包相当于是你把一个球任意削去多个面使其没有弧度
闵可夫斯基和
- 给定两个点集 \(A,B\),其闵和为:
\[A + B = \{ \overrightarrow{a} + \overrightarrow{c} \>\mid\> \overrightarrow{a} \in A, \overrightarrow{c} \in B \}
\]
- 即把每个点都做向量加
- 当 \(A,B\) 都是凸包上的点(后称凸集),那么其闵和也是凸集,且其边集是由凸集 \(A,B\) 的边按极角排序后连接的结果
- 这个东西进行 \(DP\) 优化很好用,你发现你的 \(DP\) 式子中的东西是凸包,就可以直接上闵和
扫描线
- 没想到吧这个也是计算几何,毕竟可以维护面积并和周长并
- 隆重推荐我的扫描线再学习
有可能这就是唯一有代码的一章了
旋转卡壳
- \(Rotating\>Calipers\) 算法,翻译叫做旋转卡壳
所以这玩意怎么念全是多音字,可以在线性复杂度内求解凸包直径、最小矩形覆盖
凸包直径
- 凸包直径:所有点对之间的最长距离
- 逆时针遍历凸包上的边,对于每条边都找到离这条边最远的点,并随边的旋转不断更新
- 对最优点的更新,由于我们确定边,那我们可以求待选两个顶点与这条边构成三角形的面积,直接比较大小
最小矩形覆盖
- 最小覆盖矩形:能够覆盖所有点的最小面积的矩形
- 我们还是进行旋转卡壳,和上面一样找到最优点,再分别维护左右两侧的点,通过与所选线点乘求投影比较,左右两投影相加就是矩形的另一个边长,这俩相互独立

后话
- 代码以后补
$\mathscr{msjing}$


浙公网安备 33010602011771号