计算几何
计算几何
基础两点:
- 实数的输入和输出。计算几何一般都是实数,编程时用double型
- 实数的精度判断。两个函数:sgn 和 dcmp
const double pi = acos(-1.0);
const double eps = 1e-8;
int sgn(double x){//判断x的大小
if(fabs(x)<eps) return 0;
else return x<0?-1:1;
}
// int dcmp(double x, double y){//比较两个浮点数
// if(fabs(x-y)<eps) return 0;
// else return x<y?-1:1;
// }
int dcmp(double x, double y){//比较两个浮点数
return sgn(x - y);
}
二维几何
完整板子
点和向量
- 集合
//>>>>点和向量
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);}
Point operator / (double k){return Point(x / k, y / k);}
bool operator == (Point B){return sgn(x - B.x) == 0 && sgn(y - B.y) == 0; }
};
//两点之间的距离
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;//命名向量
- 点
二维平面中的点,用坐标(x,y)来表示
struct Point{
double x, y;
Point(){}
Point(double x, double y):x(x), y(y){}
};
- 两点之间的距离
勾股定理,三角形斜边
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);}
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);}
bool operator == (Point B){return sgn(x-B.x)==0 && sgn(y-B.y)==0;}
点积和叉积
- 集合
//>>>>点积和叉积
double Dot(Vector A, Vector B){ return A.x * B.x + A.y * B.y; }//点积
double Len(Vector A){return sqrt(Dot(A, A));}//求向量长度
double Len2(Vector A){return Dot(A, A);}//求向量长度的平方,避免精度问题
double Angle(Vector A, Vector B){return acos(Dot(A, B) / Len(A) / Len(B));}//求AB角度
double Cross(Vector A, Vector B){return A.x * B.y - A.y * B.x;}//叉积
double Area2(Point A, Point B, Point C){ return Cross(B - A, C - A);}//四边形面积
double Area(Point A, Point B, Point C){ return Area2(A, B, C) / 2.0;}//三角形面积
bool Parallel(Vector A, Vector B){return sgn(Cross(A, B)) == 0;} //返回true表示平行或重合
- 点积 Dot Product
$A·B = |A| |B| \cos\theta = A.x \times B.x + A.y \times B.y $
double Dot(Vector A,Vector B){ return A.x*B.x + A.y*B.y; }
- 点积的应用
- 判向量 A 与 B 的角度
若 \(Dot(A,B) > 0\),A 与 B 的夹角为锐角
若 \(Dot(A,B) = 0\),A 与 B 的夹角为直角
若 \(Dot(A,B) < 0\),A 与 B 的夹角为钝角 - 求向量长度 \(|A| = Dot(A,A)\)
//求长度
double Len(Vector A){return sqrt(Dot(A,A));}
//求长度的平方,避免精度问题
double Len2(Vector A){return Dot(A,A);}
- 求向量 A 与 B 的夹角大小
$\theta = \arccos( \frac{AB}{|A||B|} ) $
double Angle(Vector A,Vector B){return acos(Dot(A,B)/Len(A)/Len(B));}
- 叉积 Cross Product
$A·B = |A| |B| \sin\theta = A.x \times B.y - A.y \times B.x $
double Cross(Vector A,Vector B){return A.x*B.y – A.y*B.x;}
A×B与B×A相反
- 叉积的应用
- 判断向量 A、B 的方向关系
若 $A\times B > 0 $,则 B 在 A 的顺时针方向
若 $A\times B = 0 $,则 A 与 B 共线
若 $A\times B < 0 $,则 B 在 A 的逆时针方向 - 计算两向量构成的平行四边形有向面积
三个点A、B、C,以A为公共点,得到2个向量B-A和C-A,它们构成的平行四边形,面积是:
double Area2(Point A,Point B,Point C){ return Cross(B-A, C-A);}
- 三角形面积为上一方法面积的一半
- 向量旋转(待确定)
Vector Rotate(Vector A, double rad){
return Vector(A.x*cos(rad)-A.y*sin(rad), A.x*sin(rad)+A.y*cos(rad));
}
- 用叉积检查两个向量是否平行或重合
bool Parallel(Vector A, Vector B){return sgn(Cross(A,B)) == 0;} //返回true表示平行或重合
点和线
- 集合
//>>>>直线的表示
struct Line{
Point p1, p2; //(1)线上的两个点
Line(){}
Line(Point p1, Point p2):p1(p1),p2(p2){}
Line(Point p, double angle){ //(2)根据一个点和倾斜角 angle 确定直线,0<=angle<pi
p1 = p;
if(sgn(angle - pi / 2) == 0){p2 = (p1 + Point(0, 1));}
else{p2 = (p1 + Point(1, tan(angle)));}
}
Line(double a, double b, double c){ //(3)ax+by+c=0
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);
}
}
};
typedef Line Segment;//命名线段
//点和直线的位置关系
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上
}
//点和线段的位置关系
bool Point_on_seg(Point p, Line v){ //点和线段:0 点不在线段v上;1 点在线段v上
return sgn(Cross(p - v.p1, v.p2 - v.p1)) == 0 && sgn(Dot(p - v.p1, p - v.p2)) <= 0;
}
//点在直线上的投影
Point Point_line_proj(Point p, Line v){
double k = Dot(v.p2 - v.p1, p - v.p1) / Len2(v.p2 - v.p1);
return v.p1 + (v.p2 - v.p1) * k;
}
//点关于直线的对称点
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);
}
//点到直线的距离
double Dis_point_line(Point p, Line v){
return fabs(Cross(p - v.p1, v.p2 - v.p1)) / Distance(v.p1, v.p2);
}
//点到线段的距离
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); //点的投影在线段上
}
//两条直线的位置关系
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 相交
}
//两条直线的交点
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);
}
//判断两个线段是否相交
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; //1相交;0不相交
}
- 直线的表示
struct Line{
Point p1,p2; //(1)线上的两个点
Line(){}
Line(Point p1,Point p2):p1(p1),p2(p2){}
Line(Point p,double angle){ //(2)根据一个点和倾斜角 angle 确定直线,0<=angle<pi
p1 = p;
if(sgn(angle – pi/2) == 0){p2 = (p1 + Point(0,1));}
else{p2 = (p1 + Point(1,tan(angle)));}
}
Line(double a,double b,double c){ //(3)ax+by+c=0
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);
}
}
};
- 线段的表示
一个点为起点,一个点为终点
typedef Line Segment;
- 点和直线的位置关系
三种关系:点在直线左侧,点在直线上,点在直线右侧
用直线上两点和点 P 构成两个向量(同一起点),再利用叉积正负判断方向
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上
}
- 点和线段的位置关系
点在不在线段上:先用叉积判断是否共线,再利用点积判断点 P 与线段上两点构成的角是否为钝角(其实应该为\(180^0\))
bool Point_on_seg(Point p, Line v){ //点和线段:0 点不在线段v上;1 点在线段v上
return sgn(Cross(p-v.p1, v.p2-v.p1)) == 0 && sgn(Dot(p – v.p1,p- v.p2)) <= 0;
}
- 点到直线的距离
先利用叉积算$p, p_1, p_2 $构成的平行四边形的面积,然后再用面积除以平行四边形的底边长,也就是线段 \(p_1p_2\) 的长度,就得到了高,即为点到直线的距离
double Dis_point_line(Point p, Line v){
return fabs(Cross(p-v.p1,v.p2-v.p1))/Distance(v.p1,v.p2);
}
- 点到线段的距离
求点到线段的距离,在以下3个距离中取最小值:p 到 A 的距离;p 到 B 的距离;p 向 AB 作垂线,如果交点在线段上,那么这个距离就是最小值。
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); //点的投影在线段上
}
- 点在直线上的投影(原理待补充,利用点积)
即为垂线段的交点
Point Point_line_proj(Point p, Line v){
double k = Dot(v.p2-v.p1,p-v.p1)/Len2(v.p2-v.p1);
return v.p1+(v.p2-v.p1)*k;
}
- 点关于直线的对称点
先求投影,再求对称点
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);
}
- 两条直线的位置关系
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 相交
}
- 两条直线的交点
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);
}
- 判断两个线段是否相交
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; //1相交;0不相交
}
- 两条线段的交点
先判断两线段是否相交,若相交,则问题转化为直线相交
多边形
- 集合
圆
三维几何
例题
二维几何
- 点到线段的最小距离 2023杭电多校第五场 1001 Typhoon
本文来自博客园,作者:Qiansui,转载请注明原文链接:https://www.cnblogs.com/Qiansui/p/17552125.html