二维几何基础

向量运算

向量 ,

  • 点积
  • 叉积

叉积符号判断方向: 逆时针方向。

struct Point {
  double x, y;
  Point(double x = 0, double y = 0) : x(x), y(y) {}
  Point operator+(const Point &p) const { return {x + p.x, y + p.y}; }
  Point operator-(const Point &p) const { return {x - p.x, y - p.y}; }
  double dot(const Point &p) const { return x * p.x + y * p.y; }
  double cross(const Point &p) const { return x * p.y - y * p.x; }
};

叉积符号与转向

叉积的符号直接反映两个向量的转向关系:

  • 左侧(逆时针),即左转
  • 右侧(顺时针),即右转
  • :两向量共线

给定三点 ,通过 判断转向:

int ccw(Point a, Point b, Point c) {
  double t = (b - a).cross(c - a);
  if (t > eps) return 1;   // 左转(逆时针)
  if (t < -eps) return -1; // 右转(顺时针)
  return 0;                // 共线
}

叉积符号是所有几何判定(线段相交、凸包、点与多边形关系)的基础。

!
!

线段相交

快速排斥实验 + 跨立实验

bool segInter(Point a, Point b, Point c, Point d) {
  if (max(a.x, b.x) < min(c.x, d.x) || max(c.x, d.x) < min(a.x, b.x)) return 0;
  if (max(a.y, b.y) < min(c.y, d.y) || max(c.y, d.y) < min(a.y, b.y)) return 0;
  double c1 = (b - a).cross(c - a), c2 = (b - a).cross(d - a);
  double c3 = (d - c).cross(a - c), c4 = (d - c).cross(b - c);
  return c1 * c2 <= 0 && c3 * c4 <= 0;
}

快速排斥实验:先判断两线段包围盒是否相交,排除明显不相交的情况。

跨立实验:利用叉积判断每条线段的两个端点是否在另一条线段的两侧。 分别表示 相对于 的方位, 分别表示 相对于 的方位。

时,两线段相交(包括端点重合的情况)。

!

点在多边形内

光线投射法:从点引射线,统计与多边形边交点个数,奇内偶外。

bool pointInPoly(Point p, vector<Point> &poly) {
  int n = poly.size(), cnt = 0;
  for (int i = 0; i < n; ++i) {
    Point a = poly[i], b = poly[(i + 1) % n];
    if (segInter(p, {1e9, p.y + 1}, a, b)) cnt++;
  }
  return cnt & 1;
}

多边形面积

利用叉积求有向面积(鞋带公式):

double polygonArea(vector<Point> &p) {
  double s = 0;
  for (int i = 0; i < (int)p.size(); ++i)
    s += p[i].cross(p[(i + 1) % p.size()]);
  return fabs(s) / 2;
}
题目说明
P1153 点和直线点、直线操作
P1355 平面几何点、线综合
路径D-DSA算法刷题更多练习

相关笔记:凸包, 半平面交

相关链接

路径D-DSA算法刷题 | 二维几何基础 | 凸包 | 半平面交

多平台练习

| 洛谷 | 本题单 | 竞赛基础 |
| POJ (北大) | PKU JudgeOnline | 经典题目,适合巩固 |
| HDU (杭电) | HDU OJ | 暑期多校训练 |
| Codeforces | Codeforces | 国际竞赛,适合提升 |