计算几何基础总结与模板(不是教程什么的,更像是个文档,看起来会很枯燥QwQ)(最底部有完整模板,可通过目录点击跳转)(更新ing)


文末有完整版的代码

二维几何基础

0. 某些需要注意的事项

计算几何中的坐标值一般是实数,所以一般情况下使用double类型来存储,但浮点数会有精度问题,在判断一个浮点数是否等于零,或者判断两个浮点数是否相等的时候,不能直接使用“==”,而是要设置一个偏差值eps。

const double eps = 1e-8;

一般取用1e-8,取1e-10的时候在某些情况下会出问题,比如HDU5572

在判断是否等于零,和比较两个浮点数是否相等时,同样也需要根据这个偏差值来判断。

const double pi = acos(-1.0);//高精度圆周率
const double eps = 1e-8;//偏差值
//判断是否等于零,返回0为等于零,返回-1为小于,1为大于
inline int sgn(double x) {
    if (fabs(x) < eps)return 0;
    else return x < 0 ? -1 : 1;
}

//判断是否相等,返回0为相等,返回-1为小于,1为大于
inline int dcmp(double x, double y) {
    if (fabs(x - y) < eps)return 0;
    else return x < y ? -1 : 1;
}

1. 点和向量

二维平面中用一组坐标来表示

struct Point {
    double x, y;

    Point() {}

    Point(double x, double y) : x(x), y(y) {}
};

两点间距离

两种求法,第一种利用库函数

inline double Distance(Point A, Point B) {
    return hypot(A.x - B.x, A.y - B.y);
}

第二种,手写

inline double Distance(Point A, Point B) {
    return sqrt((A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y));
}

向量

可以直接用一组二维坐标来表示二维平面上的向量

typedef Point Vector;

向量运算

直接在Point结构体进行运算符重载

点与点相加没有意义,点与向量相加得到另一个点,向量与向量相加得到另一个向量

    Point operator+(Point B) { return Point(x + B.x, y + B.y); }

点与点相减是一个向量,向量A减向量B的结果是B指向A的向量

    Point operator-(Point B) { return Point(x - B.x, y - B.y); }
乘除

向量与实数相乘得到等比例放大的向量,相除得到等比例缩小的向量

    Point operator*(double k) { return Point(x * k, y * k); }

    Point operator/(double k) { return Point(x / k, y / k); }

2. 点积和叉积

点积

几何意义为A在B上的投影

inline double dot(Vector A, Vector B) {
    return A.x * B.x + A.y * B.y;
}

点积的应用

判断向量a和b夹角类型

dot(a, b) > 0,锐角
dot(a, b) == 0,直角
dot(a, b) < 0,钝角

求向量A长度
//向量长度
inline double vector_length(Vector A) {
    return sqrt(Dot(A, A));
}

//向量长度平方
inline double vector_length_square(Vector A) {
    return Dot(A, A);
}
求向量A和向量B的夹角大小
//向量夹角
inline double Angle(Vector A, Vector B) {
    return acos(Dot(A, B) / vector_length(A) / vector_length(B));
}

叉积

几何意义为向量A和向量B构成的平行四边形的“有向”面积,有正负,A × B = -B × A。

//向量叉积
inline double Cross(Vector A, Vector B) {
    return A.x * B.y - A.y * B.x;
}

叉积的基本应用

向量A和B的方向关系

A × B > 0,B在A的逆时针方向
A × B < 0,B在A的顺时针方向
A × B = 0,A、B共线,可能同向,也可能反向

计算三点构成平行四边形与三角形的面积

以A为公共点, A B → , A C → \overrightarrow{AB},\overrightarrow{AC} AB AC 为向量构成的平行四边形面积

//三点构成平行四边形面积(A为公共点)
inline double Area(Point A, Point B, Point C) {
    return Cross(B - A, C - A);
}

以B或C构成的平行四边形面积相等,但正负不同。
以ABC为顶点构成的三角形面积为Area()/2

向量旋转

使向量A逆时针旋转角度rad(弧度制):

//向量逆时针旋转
inline Vector Rotate(Vector A, double rad) {
    return Vector(A.x * cos(rad) - A.y * sin(rad), A.x * sin(rad) + A.y * cos(rad));
}

逆时针旋转90°时,返回Vector(-A.y, A.x)
顺时针旋转90°时,返回Vector(A.y, -A.x)

单位法向量

逆时针旋转90°,然后取单位值

//单位法向量
inline Vector Normal(Vector A) {
    return Vector(-A.y / vector_length(A), A.x / vector_length(A));
}
检查两个向量是否平行或重合

叉积为0则重合

//检查向量平行或重合
inline bool Parallel(Vector A, Vector B) {
    return sgn(Cross(A, B)) == 0;
}

3. 点和线

直线

//直线
struct Line {
    Point p1, p2;

    Line() {}
    
    //根据端点确定直线
    Line(Point p1, Point p2) : p1(p1), p2(p2) {}
    
    //根据一个点和倾斜角angel确定直线,0 <= angel < pi
    Line(Point p, double angel) {
        p1 = p;
        if (sgn(angel - pi / 2) == 0)p2 = (p1 + Point(0, 1));
        else p2 = p1 + Point(1, tan(angel));
    }

    //ax + by + c = 0
    Line(double a, double b, double c) {
        if (sgn(a) == 0) {
            p1 = Point(0, -c / b);
            p2 = Point(1, -c / b);
        } else if (sgn(b) == 0) {
            p1 = Point(-c / a, 0);
            p2 = Point(-c / a, 1);
        } else {
            p1 = Point(0, -c / b);
            p2 = Point(1, (-c - a) / b);
        }
    }
};

线段

//线段,p1起点,p2终点
typedef Line Segment;

点和直线的位置关系

//点和直线关系:1 在左侧;2 在右侧;0 在直线上。根据视线从p1向p2看的左右
int Point_line_relation(Point p, Line v) {
    int c = sgn(Cross(p - v.p1, v.p2 - v.p1));
    if (c < 0)return 1;       //1:p在v的左边
    if (c > 0)return 2;       //2:p在v的右边
    return 0;                //0:p在v上
}

点和线段的位置关系

// 点和线段的关系:0 点p不在线段v上;1 点p在线段v上。
bool Point_on_seg(Point p, Line v){
    return sgn(Cross(p-v.p1, v.p2-v.p1)) == 0 && sgn(Dot(p - v.p1,p- v.p2)) <= 0;
}

点到直线距离

用叉积求点p,和p1、p2构成的平行四边形面积,再除以p1到p2的距离,也就是平行四边形的底,就能得到平行四边形的高,也是点到直线距离

 //点到直线的距离
 double Dis_point_line(Point p, Line v){
    return fabs(Cross(p-v.p1,v.p2-v.p1))/Distance(v.p1,v.p2);
 }

点在直线上的投影

//点在直线上的投影
Point Point_line_proj(Point p, Line v) {
    double k = Dot(v.p2 - v.p1, p - v.p1) / vector_length_square(v.p2 - v.p1);
    return v.p1 + (v.p2 - v.p1) * k;
}

点关于直线的对称点

//点到线段的距离
double Dis_point_seg(Point p, Segment v){
    if(sgn(Dot(p- v.p1,v.p2-v.p1))<0 || sgn(Dot(p- v.p2,v.p1-v.p2))<0) //点的投影不在线段上
        return min(Distance(p,v.p1),Distance(p,v.p2));
    return Dis_point_line(p,v); //点的投影在线段上
 }

两直线的位置关系

//两直线关系:0 平行,1 重合,2 相交
int Line_relation(Line v1, Line v2){
    if(sgn(Cross(v1.p2-v1.p1,v2.p2-v2.p1)) == 0){
       if(Point_line_relation(v1.p1,v2)==0)  return 1;//1 重合
       else return 0;//0 平行
    }
    return 2; //2 相交
}

求两直线交点

//求两直线ab和cd的交点
//调用前要保证两直线不平行或重合
//叉积为零则平行或重合
inline Point Cross_point(Point a, Point b, Point c, Point d) { //Line1:ab,  Line2:cd
    double s1 = Cross(b - a, c - a);
    double s2 = Cross(b - a, d - a);  //叉积有正负
    return Point(c.x * s2 - d.x * s1, c.y * s2 - d.y * s1) / (s2 - s1);
}

判断线段是否相交

//两线段是否相交:1 相交,0不相交
inline bool Cross_segment(Point a, Point b, Point c, Point d) {//Line1:ab,  Line2:cd
    double c1 = Cross(b - a, c - a), c2 = Cross(b - a, d - a);
    double d1 = Cross(d - c, a - c), d2 = Cross(d - c, b - c);
    return sgn(c1) * sgn(c2) < 0 && sgn(d1) * sgn(d2) < 0;//注意交点是端点的情况不算在内
}

线段交点

先判断是否相交,若相交,使用求直线交点的函数

4. 多边形

const int maxp = 1010;//点的数量
struct Polygon {
    int n;   //多边形的顶点数
    Point p[maxp];   //多边形的点
    Line v[maxp];    //多边形的边
};

判断点是否在多边形内部

//判断点和任意多边形的关系: 3 点上; 2 边上; 1 内部; 0 外部
int Point_in_polygon(Point pt,Point *p,int n){ //点pt,多边形Point *p
    for(int i = 0;i < n;i++){  //点在多边形的顶点上
        if(p[i] == pt)return 3;
    }
    for(int i = 0;i < n;i++){//点在多边形的边上
        Line v=Line(p[i],p[(i+1)%n]);
        if(Point_on_seg(pt,v)) return 2;
    }
    int num = 0;
    for(int i = 0;i < n;i++){
        int j = (i+1)% n;
        int c = sgn(Cross(pt-p[j],p[i]-p[j]));
        int u = sgn(p[i].y - pt.y);
        int v = sgn(p[j].y - pt.y);
        if(c > 0 && u < 0 && v >=0) num++;
        if(c < 0 && u >=0 && v < 0) num--;
    }
    return num != 0; //1 内部; 0 外部
}

多边形面积

//多边形面积
double Polygon_area(Point *p, int n) { //Point *p表示多边形。从原点开始划分三角形
    double area = 0;
    for (int i = 0; i < n; i++)
        area += Cross(p[i], p[(i + 1) % n]);
    return area / 2;    //面积有正负,不能简单地取绝对值
}

多边形重心

将多边形三角剖分,算出每个三角形重心(三角形重心是3点坐标平均值)
对每个三角形有向面积求加权平均值

//多边形重心
Point Polygon_center(Point *p, int n) { //求多边形重心。Point *p表示多边形。
    Point ans(0, 0);
    if (Polygon_area(p, n) == 0) return ans;
    for (int i = 0; i < n; i++)
        ans = ans + (p[i] + p[(i + 1) % n]) * Cross(p[i], p[(i + 1) % n]); //面积有正负
    return ans / Polygon_area(p, n) / 6.;
}

例题

HDU-1115
题解

完整模板(更新ing)

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const double pi = acos(-1.0);//高精度圆周率
const double eps = 1e-8;//偏差值
const int maxp = 1010;//点的数量
//判断是否等于零,返回0为等于零,返回-1为小于,1为大于
inline int sgn(double x) {
    if (fabs(x) < eps)return 0;
    else return x < 0 ? -1 : 1;
}

//判断是否相等,返回0为相等,返回-1为小于,1为大于
inline int dcmp(double x, double y) {
    if (fabs(x - y) < eps)return 0;
    else return x < y ? -1 : 1;
}

//---------平面几何:点和线--------------
struct Point {
    double x, y;

    Point() {}

    Point(double x, double y) : 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); }//放大k倍

    Point operator/(double k) { return Point(x / k, y / k); }//缩小k倍

    bool operator==(Point B) { return sgn(x - B.x) == 0 && sgn(y - B.y) == 0; }
};

//两点距离
inline double Distance(Point A, Point B) {
    return hypot(A.x - B.x, A.y - B.y);
}

//二维向量
typedef Point Vector;

//向量点积
inline double Dot(Vector A, Vector B) {
    return A.x * B.x + A.y * B.y;
}

//向量长度
inline double vector_length(Vector A) {
    return sqrt(Dot(A, A));
}

//向量长度平方
inline double vector_length_square(Vector A) {
    return Dot(A, A);
}

//向量夹角
inline double Angle(Vector A, Vector B) {
    return acos(Dot(A, B) / vector_length(A) / vector_length(B));
}

//向量叉积;大于0,B在A逆时针方向;等于0,A、B重合
inline double Cross(Vector A, Vector B) {
    return A.x * B.y - A.y * B.x;
}

//三点构成平行四边形面积(A为公共点)
inline double Area(Point A, Point B, Point C) {
    return Cross(B - A, C - A);
}

//向量逆时针旋转
inline Vector Rotate(Vector A, double rad) {
    return Vector(A.x * cos(rad) - A.y * sin(rad), A.x * sin(rad) + A.y * cos(rad));
}

//单位法向量
inline Vector Normal(Vector A) {
    return Vector(-A.y / vector_length(A), A.x / vector_length(A));
}

//检查向量平行或重合
inline bool Parallel(Vector A, Vector B) {
    return sgn(Cross(A, B)) == 0;
}

//直线
struct Line {
    Point p1, p2;

    Line() {}

    //根据端点确定直线
    Line(Point p1, Point p2) : p1(p1), p2(p2) {}

    //根据一个点和倾斜角angel确定直线,0 <= angel < pi
    Line(Point p, double angel) {
        p1 = p;
        if (sgn(angel - pi / 2) == 0)p2 = (p1 + Point(0, 1));
        else p2 = p1 + Point(1, tan(angel));
    }

    //ax + by + c = 0
    Line(double a, double b, double c) {
        if (sgn(a) == 0) {
            p1 = Point(0, -c / b);
            p2 = Point(1, -c / b);
        } else if (sgn(b) == 0) {
            p1 = Point(-c / a, 0);
            p2 = Point(-c / a, 1);
        } else {
            p1 = Point(0, -c / b);
            p2 = Point(1, (-c - a) / b);
        }
    }
};

//线段,p1起点,p2终点
typedef Line Segment;

//点和直线关系:1 在左侧;2 在右侧;0 在直线上。根据视线从p1向p2看的左右
inline int Point_line_relation(Point p, Line v) {
    int c = sgn(Cross(p - v.p1, v.p2 - v.p1));
    if (c < 0)return 1;       //1:p在v的左边
    if (c > 0)return 2;       //2:p在v的右边
    return 0;                //0:p在v上
}

// 点和线段的关系:0 点p不在线段v上;1 点p在线段v上。
inline bool Point_on_seg(Point p, Line v) {
    return sgn(Cross(p - v.p1, v.p2 - v.p1)) == 0 && sgn(Dot(p - v.p1, p - v.p2)) <= 0;
}

//点到直线的距离
inline double Dis_point_line(Point p, Line v) {
    return fabs(Cross(p - v.p1, v.p2 - v.p1)) / Distance(v.p1, v.p2);
}

//点在直线上的投影
inline Point Point_line_proj(Point p, Line v) {
    double k = Dot(v.p2 - v.p1, p - v.p1) / vector_length_square(v.p2 - v.p1);
    return v.p1 + (v.p2 - v.p1) * k;
}

//点p对直线v的对称点
inline Point Point_line_symmetry(Point p, Line v) {
    Point q = Point_line_proj(p, v);
    return Point(2 * q.x - p.x, 2 * q.y - p.y);
}

//点到线段的距离
inline double Dis_point_seg(Point p, Segment v) {
    if (sgn(Dot(p - v.p1, v.p2 - v.p1)) < 0 || sgn(Dot(p - v.p2, v.p1 - v.p2)) < 0) //点的投影不在线段上
        return min(Distance(p, v.p1), Distance(p, v.p2));
    return Dis_point_line(p, v); //点的投影在线段上
}

//两直线关系:0 平行,1 重合,2 相交
inline int Line_relation(Line v1, Line v2) {
    if (sgn(Cross(v1.p2 - v1.p1, v2.p2 - v2.p1)) == 0) {
        if (Point_line_relation(v1.p1, v2) == 0) return 1;//1 重合
        else return 0;//0 平行
    }
    return 2; //2 相交
}

//求两直线ab和cd的交点
//调用前要保证两直线不平行或重合
//叉积为零则平行或重合
inline Point Cross_point(Point a, Point b, Point c, Point d) { //Line1:ab,  Line2:cd
    double s1 = Cross(b - a, c - a);
    double s2 = Cross(b - a, d - a);  //叉积有正负
    return Point(c.x * s2 - d.x * s1, c.y * s2 - d.y * s1) / (s2 - s1);
}

//两线段是否相交:1 相交,0不相交
inline bool Cross_segment(Point a, Point b, Point c, Point d) {//Line1:ab,  Line2:cd
    double c1 = Cross(b - a, c - a), c2 = Cross(b - a, d - a);
    double d1 = Cross(d - c, a - c), d2 = Cross(d - c, b - c);
    return sgn(c1) * sgn(c2) < 0 && sgn(d1) * sgn(d2) < 0;//注意交点是端点的情况不算在内
}

//---------------平面几何:多边形----------------
struct Polygon {
    int n;   //多边形的顶点数
    Point p[maxp];   //多边形的点
    Line v[maxp];    //多边形的边
};

//判断点和任意多边形的关系: 3 点上; 2 边上; 1 内部; 0 外部
int Point_in_polygon(Point pt, Point *p, int n) { //点pt,多边形Point *p
    for (int i = 0; i < n; i++) {  //点在多边形的顶点上
        if (p[i] == pt)return 3;
    }
    for (int i = 0; i < n; i++) {//点在多边形的边上
        Line v = Line(p[i], p[(i + 1) % n]);
        if (Point_on_seg(pt, v)) return 2;
    }
    int num = 0;
    for (int i = 0; i < n; i++) {
        int j = (i + 1) % n;
        int c = sgn(Cross(pt - p[j], p[i] - p[j]));
        int u = sgn(p[i].y - pt.y);
        int v = sgn(p[j].y - pt.y);
        if (c > 0 && u < 0 && v >= 0) num++;
        if (c < 0 && u >= 0 && v < 0) num--;
    }
    return num != 0; //1 内部; 0 外部
}

//多边形面积
double Polygon_area(Point *p, int n) { //Point *p表示多边形。从原点开始划分三角形
    double area = 0;
    for (int i = 0; i < n; i++)
        area += Cross(p[i], p[(i + 1) % n]);
    return area / 2;    //面积有正负,不能简单地取绝对值
}

//多边形重心
//将多边形三角剖分,算出每个三角形重心(三角形重心是3点坐标平均值)
//对每个三角形有向面积求加权平均值
Point Polygon_center(Point *p, int n) { //求多边形重心。Point *p表示多边形。
    Point ans(0, 0);
    if (Polygon_area(p, n) == 0) return ans;
    for (int i = 0; i < n; i++)
        ans = ans + (p[i] + p[(i + 1) % n]) * Cross(p[i], p[(i + 1) % n]); //面积有正负
    return ans / Polygon_area(p, n) / 6.;
}

int main() {
    //ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值