zoukankan      html  css  js  c++  java
  • 二维计算几何基础

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

    以下全为抄课件

    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
  • 相关阅读:
    自动化运维-ansible
    Linux中安装wget命令
    Celery
    SQLAlchemy
    非关系型数据库--MongoDB
    Flask框架
    Redis
    公钥与私钥
    win10 文件扩展名的更改
    if 和 elif 的区别
  • 原文地址:https://www.cnblogs.com/zdsrs060330/p/15080405.html
Copyright © 2011-2022 走看看