文章目录
-
- 六、计算几何
-
- [6.1 基础几何模板](#6.1 基础几何模板)
- [6.2 点到线段距离 / 线段相交](#6.2 点到线段距离 / 线段相交)
- [6.3 凸包(Andrew 算法)](#6.3 凸包(Andrew 算法))
六、计算几何
6.1 基础几何模板
cpp
class Solver {
public:
const double eps = 1e-8;
const double PI = acos(-1.0);
int sign(double x) {
if (fabs(x) < eps) return 0;
return x < 0 ? -1 : 1;
}
struct Point {
double x, y;
Point(double x = 0, double y = 0) : x(x), y(y) {}
Point operator+(Point b) { return Point(x + b.x, y + b.y); }
Point operator-(Point b) { return Point(x - b.x, y - b.y); }
Point operator*(double k) { return Point(x * k, y * k); }
double operator*(Point b) { return x * b.x + y * b.y; } // 点积
double operator^(Point b) { return x * b.y - y * b.x; } // 叉积
double len() { return sqrt(x*x + y*y); }
double len2() { return x*x + y*y; }
};
// 叉积:ab × ac
double cross(Point a, Point b, Point c) {
return (b - a) ^ (c - a);
}
// 点积:ab · ac
double dot(Point a, Point b, Point c) {
return (b - a) * (c - a);
}
// 两点距离
double dist(Point a, Point b) {
return (a - b).len();
}
};
思路要点:
- 点积:判断夹角(锐角/钝角/垂直)、投影长度
- 叉积:判断转向(顺/逆时针)、平行四边形面积
- 浮点比较用 sign 函数,避免精度问题
6.2 点到线段距离 / 线段相交
cpp
class Solver {
public:
// 点p到线段ab的距离
double point_to_seg(Point p, Point a, Point b) {
if (sign(dot(a, b, p)) < 0) return dist(p, a);
if (sign(dot(b, a, p)) < 0) return dist(p, b);
return fabs(cross(a, b, p)) / dist(a, b);
}
// 判断线段相交(规范相交)
bool seg_intersect(Point a1, Point a2, Point b1, Point b2) {
double c1 = cross(a1, a2, b1), c2 = cross(a1, a2, b2);
double c3 = cross(b1, b2, a1), c4 = cross(b1, b2, a2);
return sign(c1) * sign(c2) < 0 && sign(c3) * sign(c4) < 0;
}
};
6.3 凸包(Andrew 算法)
cpp
class Andrew {
public:
vector<Point> convex_hull(vector<Point> p) {
int n = p.size(), k = 0;
if (n <= 1) return p;
sort(p.begin(), p.end(), [](Point a, Point b) {
return sign(a.x - b.x) ? a.x < b.x : a.y < b.y;
});
vector<Point> res(n * 2);
// 下凸壳
for (int i = 0; i < n; res[k++] = p[i++])
while (k >= 2 && sign(cross(res[k-2], res[k-1], p[i])) <= 0) k--;
// 上凸壳
for (int i = n - 2, t = k + 1; i >= 0; res[k++] = p[i--])
while (k >= t && sign(cross(res[k-2], res[k-1], p[i])) <= 0) k--;
res.resize(k - 1);
return res;
}
};
思路要点:
- 按 x 排序,x 相同按 y
- 从左到右扫下凸壳,右到左扫上凸壳
- 栈内维护凸性,不满足则弹出
- 时间复杂度 O(n log n)