二维计算几何基础

感觉写这个总是怀疑自己数学是不是完蛋了

以下全为抄课件

code
struct Point;
typedef Point Vector;
struct Point{
	double x, y;
	void in() {
		scanf("%lf%lf", &x, &y);
	}
	void print() {
		printf("%.2lf %.2lf
", x, y);
	}
	Point(double x = 0, double y = 0) : x(x), y(y) {
	}
    //坐标旋转
	inline Vector rotate(double ang) {
		return Vector(x * cos(ang) - y * sin(ang), x * sin(ang) + y * cos(ang));
	}
	inline double dot(const Vector &a) {
		return x * a.x + y * a.y;
	}
	inline bool operator == (const Point &a) const {
		return sgn(x - a.x) == 0 && sgn(y - a.y) == 0;
	}
	inline bool operator < (const Point &a) const {
		return sgn(x - a.x) < 0 || sgn(x - a.x) == 0 && sgn(y - a.y) < 0;
	}
    inline Vector operator + (const Vector &a) const {
		return Vector(x + a.x, y + a.y);
	}
	inline Vector operator - (const Vector &a) const {
		return Vector(x - a.x, y - a.y);
	}
    //叉积
	inline double operator * (const Vector &a) const {
		return x * a.y - y * a.x;
	}
	inline Vector operator * (double t) const {
		return Vector(x * t, y * t);
	}
	inline Vector operator / (double t) {
		return Vector(x / t, y / t);
	}
	inline double vlen() {
		return sqrt(x * x + y * y);
	}
	inline Vector norm() {
		return Point(-y, x);
	}
};
//直线相交
point intersect(point P, Vector v, point Q, Vector w) {
  point p;
  Vector u = P - Q;
  double t = (w * u) / (v * w);
  p = P + v * t;
  return p;
}
//凸包
void gao(){
    if(n < 3){
        return;
    }
    std::sort(p, p + n);
    std::copy(p, p + n - 1, p + n);
    std::reverse(p + n, p + 2 * n - 1);
    int m = 0, top = 0;
    for(int i = 0; i < 2 * n - 1; i++) {
        while(top >= m + 2 && sgn((p[top - 1] - p[top - 2]) * (p[i] - p[top - 2])) <= 0) {
            top--;
        }
        p[top++] = p[i];
        if(i == n - 1) {
            m = top - 1;
        }
    }
	n = top - 1;
}
//半平面交
int inhalfplane(point p,point s,point e) {
	return sgn((e - s) * (p - s)) ;
}
std::vector<point> CUT(const std::vector<point> &p, point s, point e) {
	//p: 当前维护的凸包,s: 加入直线的起点, e: 加入直线的终点
	//可用的一侧平面在直线左侧。
	//分类讨论可推导出。
	std::vector<point> q;
	int n = (int) p.size();
	for(int i = 0; i < n; i++) {
		int nowin = inhalfplane(p[i], s, e);
		int nextin = inhalfplane(p[(i + 1) % n], s, e);
		if(nowin >= 0) {
			q.push_back(p[i]);
		}
		if(nextin * nowin < 0) {
			q.push_back(intersect(p[i], p[(i + 1) % n] - p[i], s, e - s));
		}
	}
	return q;
}
//多边形判断是否在内部
bool point_in_polygon(Point o, Point *p, int n) {
    int t;
    Point a, b;
    p[n] = p[0];
    for(int i = 0; i < n; i++) {
        if(dot_on_seg(o, Seg(p[i], p[i + 1]))) {
            return true;
        }
    }
    t = 0;
    for(int i = 0; i < n; i++) {
        a = p[i]; b = p[i + 1];
        if(a.y > b.y) {
            std::swap(a, b);
        }
        if(sgn((a - o) * (b - o)) < 0 && sgn(a.y - o.y) < 0 && sgn(o.y - b.y) <= 0) {
            t++;
        }
    }
    return t & 1;
}

基础知识:向量,点积,叉积,直线(线段)相交

向量

(A(x_1,y_1)) 指向 (B(x_2,y_2)) 的向量为 ((x_2-x_1,y_2-y_1))
一个向量可以表示两个点之间的方向,以及长度

点积(内积)

两个向量 (A,B) 做点积等价于 (x_1*x_2+y_1*y_2=|A||B|*cos( heta))

点积也用来求两个向量的夹角

还可以用来求一个向量在另一个向量上的投影

叉积(外积)

两个向量 (A(x_1,y_1), B(x_2,y_2)) 的叉积可以表示为 (x_1*y_2-x_2*y_1)

叉积的值如果为正,表示从B向量在A向量的左边;如果为负,表示B向量在A向量的右边。

即假设A,B向量都从原点O出发,那么 从OA到OB如果在顺时针180度范围内,叉积就为正,否则为负

直线相交

求两直线交点需要先判断是否平行或者共线

求交点时可以利用参数方程来做

将第一条直线上的点表示成 ((P.x+v.x * t_1, P.y + v.y * t_1))​​ 的形式

将第二条直线上的点表示成 ((Q.x+w.x*t_2,Q.y+w.y * t_2)) 的形式

两项分别相等可以解出 (t_1)(t_2),就可以求出交点

求线段交点可以先求直线交点 再判断交点是否在线段上

坐标旋转

((x_1, y_1))((x_0, y_0)) 为中心逆时针旋转 (a)

等价于 ((x_1-x_0, y_1-y_0))​ 绕原点旋转,求出旋转后的坐标加上 ((x_0,y_0)) 即可

((x,y)) 绕原点逆时针旋转 (a) 度会得到

((x*cos(a) - y * sin(a), x*sin(a) + y * cos(a)))

凸包与半平面交

凸包

常见求凸包的方法有两种:水平序和极角序

水平序是先求下凸折线,再求上凸折线,最后拼成凸包

极角序是选择一个左下角的点当作原点,所有的点按照 与原点连线后跟x轴正向的角度 排序(此处可以学一下atan2函数)

排序之后的操作都差不多,下面以水平序为例

凸包的思想也经常用于其它与单调性有关的算法中,比如斜率优化


将凸包分成上下两条凸折线

用一个栈记录折线上的点,假设栈顶的元素为b,前一个为a

假设当前点为c

如果 (cross(a, b, c) ge 0) 则直接将c加入栈中

否则将b退栈,直到 (cross(a,b,c) ge 0)

半平面交

有若干个 (ax+by+c = 0) 的直线, 每个直线有一侧是我们需要的一个半平面,求所有半平面的交集(一个凸多边形)

(n^2)(nlog n) 两种做法

此处介绍 (n^2) 做法

维护一个凸包,表示半平面的交,

每次新来一条直线,暴力找到这条直线会与哪条凸包上的边相交(如果某条边两个端点在直线的异侧,肯定有交点)

然后求出新的交点,维护新的凸包

(nlog n) 的算法使用双端队列优化,可参考刘汝佳训练指南

其他:三角形,多边形,坐标旋转

多边形

求多边形的面积(不一定是凸的):随便按照一个方向求相邻 三个点构成的有向三角形的面积之和, 最后取绝对值

(O(log n))​​ 判断点是否在凸多边形内部:二分判断点在哪一块三角形区域内,最后再判断点是否在三角形内部

判断点在任意⼀个多边形内部:射线法,做一条线,判断与多边形相交几次,奇数次就表示在多边形内部,否则在外部

qaqaq
原文地址:https://www.cnblogs.com/zdsrs060330/p/15080405.html