加载中...

计算几何基础

蒟蒻的计算几何几乎不会,这里记录一些计算几何的常见概念,以作备忘:

oi-wiki

向量

可以用一个二维坐标 \((x, y)\) 表示。比如点 \(A = (x, y)\),则 \(\overrightarrow{OA} = (x, y)\)

\(A(x_{1}, y_{1}), B(x_{2}, y_{2})\) \(\rightarrow\) \(\overrightarrow{AB} = (x_{2} - x_{1}, y_{2} - y_{1})\)

运算:\(\overrightarrow{AB} = \overrightarrow{OB} - \overrightarrow{OA}\)

二维向量运算可以看作是 \(x,y\) 两个方向上的加减运算复合,三维同理。

代码:

// 注意,Point类型既可以表示二维平面中的一个点,也可以表示一个向量
struct Point{
    double x, y;
    Point(double x, double y): x(x), y(y){}
};

// 向量加法
Point operator + (Point a, Point b){
    return Point(a.x + b.x, a.y + b.y);
}

// 向量减法
Point operator - (Point a, Point b){
    return Point(a.x - b.x, a.y - b.y);
}

// 数乘
Point operator * (Point a, double t){
    return Point(a.x * t, a.y * t);
}

点积

形式:\(\overrightarrow{a} \cdot \overrightarrow{b} = x_{1}x_{2} + y_{1}y_{2} = |a||b| \cos\theta\)

几何意义:\(\overrightarrow{b}\)\(\overrightarrow{a}\) 上的投影 与 \(\overrightarrow{a}\) 的长度 的乘积

代码:

// 求点积
double dot(Point a, Point b){
    return a.x * b.x + a.y * b.y;
}

// 求模长
double len(Point a){ // 注意这里a代表一个向量
    return sqrt(a.x * a.x + a.y * a.y);
}

// 求夹角
double angle(Point a, Point b){
    return acos(dot(a, b) / len(a) / len(b));
}

应用:

  • 判断两向量垂直:\(\overrightarrow{a} \cdot \overrightarrow{b} = 0\)
  • 判断两向量平行(共线):\(\overrightarrow{a} \cdot \overrightarrow{b} = |\overrightarrow{a}||\overrightarrow{b}|\)
  • 求两向量的夹角:\(\cos \theta = \frac{\overrightarrow{a} \cdot \overrightarrow{b}}{|\overrightarrow{a}||\overrightarrow{b}|}\)

叉积

形式:\(\overrightarrow{a} \times \overrightarrow{b} = x_{1}y_{2} - x_{2}y_{1} = |a||b| \sin\theta\)

几何意义:\(\overrightarrow{a}\)\(\overrightarrow{b}\) 张成的平行四边形的有向面积。\(\overrightarrow{b}\)\(\overrightarrow{a}\) 的顺时针方向(右侧)为负;\(\overrightarrow{b}\)\(\overrightarrow{a}\) 的逆时针方向(左侧)为正。

注意,这里的左,右侧是相对于朝着向量方向而言。

代码:

// 求叉积
double det(Point a, Point b){
    return a.x * b.y - b.x * a.y;
}
// 向量ab 与 向量ac 的叉积
double cross(Point a, Point b, Point c){
    return det(b - a, c - a);
}
// 判断点c在直线ab的哪一侧(1为左侧,-1为右侧,0为三点共线)
int sgn(double x){
    if(x == 0) return 0;
    return (x > 0 ? 1 : -1);
}
int direction(Point a, Point b, Point c){
    return sgn(cross(a, b, c));
}

// 判断直线ab与线段cd是否有交点:
bool iscross(Point a, Point b, Point c, Point d){
    return cross(a, b, c) * cross(a, b, d) <= 0;
}

// 判断线段ab与线段cd是否有交点
bool iscross(Point a, Point b, Point c, Point d){
    return cross(a, b, c) * cross(a, b, d) <= 0
        && cross(c, d, a) * cross(c, d, b) <= 0;
}

// 得到直线au与直线bv的交点坐标(共线时分母为0,需特判)
Point getcrossnode(Point a, Point u, Point b, Point v){
    double t = det((a - b), v) / det(v, u); // 比例
    return a + u * t;
}

应用:

  1. 判定点线之间的位置关系
  • \((\overrightarrow{b} - \overrightarrow{a}) \times (\overrightarrow{c} - \overrightarrow{a}) > 0\) \(\rightarrow\)\(c\) 在向量 \(\overrightarrow{ab}\) 的左侧
  • \((\overrightarrow{b} - \overrightarrow{a}) \times (\overrightarrow{c} - \overrightarrow{a}) < 0\) \(\rightarrow\)\(c\) 在向量 \(\overrightarrow{ab}\) 的右侧
  • \((\overrightarrow{b} - \overrightarrow{a}) \times (\overrightarrow{c} - \overrightarrow{a}) = 0\) \(\rightarrow\) 三点共线
  1. 判定线线之间的位置关系
  • 判断线段 \(ab\) 与 线段 \(cd\) 是否有交点:
    (1) \(cross(a, b, c) * cross(a, b, d) > 0\)\(cross(c, d, a) * cross(c, d, b) > 0\) \(\rightarrow\) 无交点
    (2)\(cross(a, b, c) * cross(a, b, d) <= 0\) 并且 \(cross(c, d, a) * cross(c, d, b) <= 0\) \(\rightarrow\) 有交点
  • 求两个线段的交点 \(\rightarrow\) 见代码部分即可。证明见下图:

pV0nM8g.png

交点 \(O\) 可以利用点 \(a\)\(\overrightarrow{au}\) 方向上的偏移得出\(|au|\) 已知,故只要知道 \(\frac{|oa|}{|ua|}\) 即可。发现它们的关系恰为两个平行四边形的高之比,而底相同,故又等于两个平行四边形的面积之比,而平行四边形的面积可以利用叉积快速得出。

  1. 求任意多边形的面积:
    pV0nDM9.png

注:上述做法只是在求多边形面积的大小。由叉积的正负性可知,其实多边形的面积也有正负性之分。而 多边形面积的正负性 可以 区分多边形中每条边两侧分别是多边形的外部/内部(一个性质:若将多边形中的所有边按照顺时针或逆时针标注方向,则它们的 左/右侧 与 多边形外部/内部 是一一对应的)。

例题:2025杭电多校9 1005 code

(待 \(upd...\)

posted @ 2025-08-15 23:41  jxs123  阅读(18)  评论(0)    收藏  举报