整理计算几何模板,以及一些题目
Update 1:基础函数,点,向量的运算
Update 2:凸包,旋转卡壳
基础的函数定义
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 const double eps = 1e-8; 2 int dcmp(double x) { 3 if (fabs(x) < eps) return 0; 4 else return x > 0 ? 1:-1; 5 }
基于点和向量的运算
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 struct Point { 2 double x, y; 3 Point(double _x, double _y):x(_x),y(_y){} 4 Point():x(0.0),y(0.0){} 5 }; 6 typedef Point Vec; 7 8 void showPoint(Point A) { 9 printf("(%.6f, %.6f)\n", A.x, A.y); 10 } 11 const Vec operator + (Vec A, Vec B) { 12 return Vec(A.x+B.x, A.y+B.y); 13 } 14 const Vec operator - (Vec A, Vec B) { 15 return Vec(A.x-B.x, A.y-B.y); 16 } 17 const double operator * (Vec A, Vec B) { 18 return A.x*B.x + A.y*B.y; 19 } 20 // A*B = |A|*|B|*sin(theta) 21 // theta为 A,B 向量的夹角 22 // 如果 A 在 B 的顺时针方向180度内,则theta取正值 23 // 向量叉乘 24 // 返回值为正时,表示 A 在 B 的右侧180度内 25 // 返回值的绝对值等于以A,B向量为邻边的平行四边型的面积 26 const double operator ^ (Vec A, Vec B) { 27 return A.x*B.y - A.y*B.x; 28 } 29 30 double Lenth(Vec &v) { 31 return sqrt(v*v); 32 } 33 34 35 // 将点 A 绕 p 逆时针旋转 theta 角度(弧度制) 36 // 1.平移坐标 37 // 2.旋转 38 // 3.平移坐标 39 Vec rotate(point A, point p, double theta) { 40 A = A-p; 41 point ret = point(A.x*cos(theta)-A.y*sin(theta), A.x*sin(theta)+A.y*cos(theta)) + p; 42 return ret; 43 } 44 45 46 // 计算C到AB的距离 47 // isSeg = 1 : AB为线段 48 // isSeg = 0 : AB为直线 49 double linePointDist(Point A, Point B, Point C, bool isSeg) { 50 double dist = ((B-A)^(C-A)) / sqrt((B-A)*(B-A)); 51 if (isSeg) { 52 double dot1 = (C-B)*(B-A); 53 if (dot1 > 0) return sqrt((B-C)*(B-C)); 54 double dot2 = (C-A)*(A-B); 55 if (dot2 > 0) return sqrt((A-C)*(A-C)); 56 } 57 return fabs(dist); 58 } 59 60 // 判断线段的两个端点是否在直线的两侧 61 bool lineCrossSeg(Point p1, Point p2, Point ls, Point le) 62 { 63 Vec ver = ls-le, v1 = p1-ls, v2 = p2-ls; 64 return dcmp((ver^v1)*(ver^v2)) <= 0; 65 } 66 67 // 判断点是否在线段上 68 // 叉积为 0 表示共线 69 bool pointOnSeg(point s1, point s2, point p, bool includeEnd) 70 { 71 if ((s1 == p || s2 == p) && !includeEnd) return false; 72 s2 = s2-s1; 73 p = p-s1; 74 return (s2^p)==0 && (s2*p)>=0; 75 76 double minx = min(s1.x, s2.x); 77 double maxx = max(s1.x, s2.x); 78 double miny = min(s1.y, s2.y); 79 double maxy = max(s1.y, s2.y); 80 81 if ((s1 == p || s2 == p) && !includeEnd) return false; 82 if (p.x-minx>=0 83 && maxx-p.x>=0 84 && p.y-miny>=0 85 && maxy-p.y>=0 86 && ((s2-s1)^(p-s1)) == 0) 87 return true; 88 else 89 return false; 90 } 91 92 93 // 判断两条线段是否相交 94 // 两次跨立试验 95 typedef pair<Point, Point> seg; 96 bool segCrossSeg(seg a, seg b, bool includeEnd) 97 { 98 Vec ver = b.X-b.Y, v1 = a.X-b.X, v2 = a.Y-b.X; 99 int tmp1 = dcmp((ver^v1)*(ver^v2)); 100 ver = a.X-a.Y, v1 = b.X-a.X, v2 = b.Y-a.X; 101 int tmp2 = dcmp((ver^v1)*(ver^v2)); 102 if (includeEnd) 103 return tmp1 <= 0 && tmp2 <= 0; 104 else 105 return tmp1 < 0 && tmp2 < 0; 106 } 107 108 double areaPoly(vector<Point> &P) { 109 double area = 0.0; 110 for (int i = 1, n = P.size(); i+1 < n; i++) { 111 area += (P[i+1]-P[0])^(P[i]-P[0]); 112 } 113 return area / 2.0; 114 }
基于直线的运算(直线方程 : Ax+By=C)
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 struct Line { 2 // Ax + By = C 3 double A, B, C; 4 Line(double _A, double _B, double _C):A(_A),B(_B),C(_C){} 5 Line():A(0.0),B(0.0),C(0.0){} 6 }; 7 8 Line getLineFromPoints(Point p1, Point p2) { 9 double A = p2.y-p1.y; 10 double B = p1.x-p2.x; 11 double C = A*p1.x + B*p1.y; 12 return Line(A,B,C); 13 } 14 15 // int = 0 无交点 16 // int = 1 一个交点 17 // int = 2 两直线重合 18 pair<Point, int> intersectPoint(Line l1, Line l2) { 19 double det = l1.A*l2.B - l2.A*l1.B; 20 if (dcmp(det) == 0) { 21 // 两直线平行 重合 22 if (dcmp(l1.A*l2.C - l2.A*l1.C) == 0 && 23 dcmp(l1.B*l2.C - l2.B*l1.C) == 0) 24 return make_pair(Point(), 2); 25 else 26 return make_pair(Point(), 0); 27 } 28 else { 29 double x = (l2.B*l1.C - l1.B*l2.C) / det; 30 double y = (l1.A*l2.C - l2.A*l1.C) / det; 31 return make_pair(Point(x,y), 1); 32 } 33 }
凸包模板(白书)
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 bool cmpxy(point a, point b) 2 { 3 if (a.x == b.x) return a.y < b.y; 4 return a.x < b.x; 5 } 6 // 包含边上的点就将 <= 改为 < 7 int convexHull(Point *p, int n) 8 { 9 sort(p, p + n, cmpxy); 10 int top = 0; 11 for (int i = 0; i < n; i++) 12 { 13 while(top>1 && ((p[s[top-1]]-p[s[top-2]])^(p[i]-p[s[top-2]])) <= 0) top--; 14 s[top++] = i; 15 } 16 int k = top; 17 for (int i=n-2;i>=0;i--) 18 { 19 while(top>k && ((p[s[top-1]]-p[s[top-2]])^(p[i]-p[s[top-2]])) <= 0) top--; 20 s[top++] = i; 21 } 22 if (n > 1) top--; 23 return top; 24 }
旋转卡壳
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 // s 为保存有序的凸包点的下标的栈 2 // 此处的凸包点为顺时针的顺序 3 int rotatingCaliper(Point *p, int top) 4 { 5 int q = 1; 6 int ans = 0; 7 s[top] = 0; 8 for (int i = 0; i < top; i++) 9 { 10 while( ((p[s[i+1]]-p[s[i]])^(p[s[q+1]]-p[s[i]])) > 11 ((p[s[i+1]]-p[s[i]])^(p[s[q]] -p[s[i]])) ) 12 q = (q+1)%top; 13 ans = max(ans, (p[s[i]]-p[s[q]])*(p[s[i]]-p[s[q]])); 14 // 处理两条边平行的情况 15 ans = max(ans, (p[s[i+1]]-p[s[q+1]])*(p[s[i+1]]-p[s[q+1]])); 16 } 17 return ans; 18 }