计算几何

前言

  • 数学统治直世界!

向量

  • 如果你会高中向量,请直接跳过到叉乘

定义

  • 向量既有大小又有方向,和物理的矢量很像,手写一般用 \(\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\)),结果是数,几何意义是一个向量在另一个向量上的投影长

2026-10-06 19-28-38屏幕截图

  • 点乘运算如下

\[\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.\)

2026-10-06 19-46-30屏幕截图

  • 其中 \(\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 \]

向量的坐标表示

  • 向量是可以用坐标表示的,如图

2026-10-06 20-17-03屏幕截图

  • \(\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}\) 然后判正负就行

两条线段位置关系

  • 平行或重合好判,不说了,关键是是否相交

快速排斥实验与跨立实验

  • 这俩可以方便判断两条线段是否相交
快速排斥实验
  • 每一条线段都有一个“影响范围”,如下

2026-10-06 20-33-52屏幕截图

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

2026-10-06 20-35-46屏幕截图

跨立实验
  • 我们知道一条线段与另一条线段相交,一条线左右端点会居于另一条线的两侧,我们依据此进行判断,这就是跨立实验,注意:两条直线的端点都必须位于另一条线的两侧,同样,这个也会判不全,所以我们需要将快速排斥实验与跨立实验结合用

点与多边形关系

光线投射算法

  • 说白了就是从点引射线,与多边形交偶数个点就在外部,奇数个就在内部

2026-10-06 20-41-05屏幕截图

  • 不过对于边自交的多边形并不适用
  • 还有一个回转数算法,不会。。。

两直线交点坐标

  • 你可以构建一次函数用解析几何的方法直接解方程,不过我们是基于线性代数的,和解析几何不属于一个区域,可能在扩展性上有一些问题
  • 我们构建三角形,通过正弦定理求解

2026-10-06 20-47-30屏幕截图

\[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}\) 就是坐标

直线与圆

  • 你仍然可以用解析几何做
  • 若有两交点,求出两交点的中点,加半个弦长

2026-10-06 21-00-07屏幕截图

圆与圆

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

2026-10-06 21-02-11屏幕截图

距离

  • 我们先介绍一下距离

欧几里得距离

  • 就是我们学的,两点 \((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 $
  • 给张图

2026-10-06 21-08-41屏幕截图

  • 曼哈顿距离可以有效保证距离是整数,并且经过任何点的 \(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 )}\)
  • 还是画图

2026-10-07 06-48-49屏幕截图

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

2026-10-07 06-55-35屏幕截图

  • 你发现其有对应关系
  • \((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]\) 的简单多边形

二维凸包

  • 我们形象化地说,就是在一个木板上钉钉子,然后用橡皮筋套上,就是凸包,画图

2026-10-07 07-08-47屏幕截图

  • 里面的点不在边上
  • 换种说法,就是找到周长最小的多边形使所有点都被包含
  • 求法有两种,我们先口胡

\(Andrew\)

  • \(Andrew\) 算法实现依据单调栈,分开处理上下凸包,为啥等会说,具体做法是:
  1. 按照 \(x\) 为第一关键字,\(y\) 为第二关键字排序,最小的元素和最大的元素一定在凸包上
  2. 下半部分,我们要让斜率尽可能地小,通过单调栈维护两条线段的斜率问题
  3. 上半同理
  • 你发现会有一些点是要求斜率小,一些要求大,所以我们分开处理,再统计点的个数
点击查看代码
#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\) 算法,翻译叫做旋转卡壳所以这玩意怎么念全是多音字,可以在线性复杂度内求解凸包直径、最小矩形覆盖

凸包直径

  • 凸包直径:所有点对之间的最长距离
  • 逆时针遍历凸包上的边,对于每条边都找到离这条边最远的点,并随边的旋转不断更新
  • 对最优点的更新,由于我们确定边,那我们可以求待选两个顶点与这条边构成三角形的面积,直接比较大小

最小矩形覆盖

  • 最小覆盖矩形:能够覆盖所有点的最小面积的矩形
  • 我们还是进行旋转卡壳,和上面一样找到最优点,再分别维护左右两侧的点,通过与所选线点乘求投影比较,左右两投影相加就是矩形的另一个边长,这俩相互独立

2026-10-07 09-33-55屏幕截图

后话

  • 代码以后补
posted @ 2026-10-07 08:20  msjing  阅读(4)  评论(1)    收藏  举报