内容参考书籍——《算法竞赛入门经典训练指南》
在平面坐标系下,向量和点一样用两个数,x和y表示。判断相等使用“三态函数”dcmp,减少精度问题。另外,向量有一个所谓的“极角”,即从x轴正半轴旋转到该方向所需的角度。c标准库里的atan2函数就是用来求极角的,例如:向量(x,y)的极角就是atan2(y,x)(单位:弧度)。
点积,高中知识。
叉积。简单地说,两个向量的叉积等于两个向量组成的三角形的有向面积的两倍。(图中带箭头的向量为v,和v同一个起点的另外一条向量为w,忘记标v和w了尴尬)
cross(v,w)>0 cross(v,w)<0
顺着第一个向量v看,,如果w在左边,那么v和w的叉积大于0,否则小于0.如果两个向量共线(方向相同),那么叉积等于0(三角形退化成为线段)。不难发现,叉积不满足交换律。事实上,cross(v,w)=-cross(w,v)。在坐标系下,两个向量的叉积等于xAyB-xByA。
位置。当我们把叉积和点积组合在一起时,我们便可以细致地判断两个向量的位置关系。第一个向量v总是水平向右的,那么另外一个点就可以用这样的方式确定位置关系(点积符号,叉积符号)。例如,当w在第二象限时点积为负但叉积均为正,用(-,+)表示。
旋转。向量可以绕起点旋转,公式为x,= xcosa-ysina , y,= xsina+ycosa。其中a为逆时针旋转的角。
单位法线。旋转90°后把长度归一化即可。
以上内容采用如下代码实现:
1 struct Point 2 { 3 double x,y; 4 Point(double x=0, double y=0):x(x),y(y) {}//构造函数,方便编写代码 5 }; 6 typedef Point Vector;//别名 7 //向量+向量=向量,点+向量=点 8 Vector operator + (Vector A, Vector B){return Vector(A.x+B.x,A.y+B.y);} 9 //点-点=向量 10 Vector operator - (Vector A, Vector B){return Vector(A.x-B.x,A.y-B.y);} 11 //向量*数=向量 12 Vector operator * (Vector A, double p){return Vector(A.x*p,A.y*p);} 13 //向量/数=向量 14 Vector operator / (Vector A, double p){return Vector(A.x/p,A.y/p);} 15 16 bool operator < (const Point& a, const Point& b){return a.x<b.x||(a.x==b.x&&a.y<b.y);} 17 18 const double eps = 1e-10; 19 int dcmp(double x) 20 { 21 if (fabs(x) < eps) return 0; 22 else return x < 0 ? -1 : 1; 23 } 24 25 bool operator == (const Point& a, const Point &b){return dcmp(a.x-b.x) == 0 && dcmp(a.y-b.y) == 0;} 26 27 //计算点积、向量长度、夹角函数 28 double Dot(Vector A, Vector B){return A.x*B.x+A.y*B.y;} 29 double Length(Vector A){return sqrt(Dot(A,A));} 30 double Angle(Vector A, Vector B){return acos(Dot(A,B)/Length(A)/Length(B));} 31 32 //计算叉积 33 double Cross(Vector A,Vector B){return A.x*B.y-A.y*B.x;} 34 double Area2(Point A, Point B, Point C){return Cross(B-A,C-A);} 35 36 //计算旋转和单位法向量 37 //rad为弧度 38 Vector Rotate(Vector A, double rad){return Vector(A.x*cos(rad)-A.y*sin(rad),A.x*sin(rad)+A.y*cos(rad));} 39 Vector Normal(Vector A) 40 { 41 double L =Length(A); 42 return Vector(-A.y/L,A.x/L); 43 }
点和直线。采用参数方程表示直线。直线上一点P0和方向向量v表示