点和线段的定义
1 2 3 4 5 6 7 8 typedef struct point { int x; int y; }Point; typedef struct segment { Point p1; Point p2; }Segment;
叉积
叉积为正,p1位于p2顺时针方向,为负,p1位于p2逆时针方向
确定两个有向线段的顺逆时针方向,将有向线段源点都转化为原点,从而比较两个点的顺逆时针方向
1 2 3 long long CrossProduct (Point p1,Point p2) { return p1.x*p2.y-p1.y*p2.x; }
线段相交
两个连续线段的左转右转问题,同样可以看叉积确定顺逆时针,pi-》pj-》pk
负数就是逆时针,正数就是顺时针
1 2 3 long long Direction (Point pi,Point pj,Point pk) { return (pk.x-pi.x)*(pj.y-pi.y)-(pk.y-pi.y)*(pj.x-pi.x); }
两条线段相交的情况:
两条线段都跨越了对方所在直线
或者一条线段的某个端点在另一条直线上(特殊情况)
返回1就是在线段上
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 int OnSegment(Point pi,Point pj,Point pk){//pk在不在线段pi,pj上 if(min(pi.x,pj.x)<=pk.x&&pk.x<=max(pi.x,pj.x)&&min(pi.y,pj.y)<=pk.y&&pk.y<=max(pi.y,pj.y))return 1; else return 0; } int SegmentIsIntersect(Point p1,Point p2,Point p3,Point p4){ long long d1 = Direction(p3,p4,p1); long long d2 = Direction(p3,p4,p2); long long d3 = Direction(p1,p2,p3); long long d4 = Direction(p1,p2,p4); if(((d1>0&&d2<0)||(d1<0&&d2>0))&&((d3<0&&d4>0)||(d3>0&&d4<0))){ return 1; } else if(d1==0&&OnSegment(p3,p4,p1))return 1; else if(d2==0&&OnSegment(p3,p4,p2))return 1; else if(d3==0&&OnSegment(p1,p2,p3))return 1; else if(d4==0&&OnSegment(p1,p2,p4))return 1; else return 0; }
点到线段的距离
包含了特殊情况
1 2 3 4 5 6 7 8 9 10 long double PointToSegment (Point p0, Point p1, Point p2) { long long cross = (p2.x - p1.x) * (p0.x - p1.x) + (p2.y - p1.y) * (p0.y - p1.y); if (cross <= 0 )return sqrt ((p0.x - p1.x) * (p0.x - p1.x) + (p0.y - p1.y) * (p0.y - p1.y)); long long d2 = (p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y); if (cross >= d2)return sqrt ((p0.x - p2.x) * (p0.x - p2.x) + (p0.y - p2.y) * (p0.y - p2.y)); long double r = (long double ) cross / d2; long double px = p1.x + (p2.x - p1.x) * r; long double py = p1.y + (p2.y - p1.y) * r; return sqrt ((p0.x - px) * (p0.x - px) + (p0.y - py) * (p0.y - py)); }
Graham凸包
首先找到y坐标最小,最靠左的点p0,p0一定是凸包上的点
对p0参考,对其它所有点排序(根据幅角,或者是余弦值)
入栈p0,p1,p2,每次看一个点相对于栈顶两个元素连线的位置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 Point p0; Point stack [10000 ]; int top = -1 ;long double Distance (Point p1,Point p2) { return sqrtl((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y)); } long long Direction (Point pi,Point pj,Point pk) { return (pk.x-pi.x)*(pj.y-pi.y)-(pk.y-pi.y)*(pj.x-pi.x); } long long area (Point p0, Point p1, Point p2) { return llabs((p1.x - p0.x) * (p2.y - p0.y) - (p1.y - p0.y) * (p2.x - p0.x)); } int cmp (const void *e1,const void *e2) { Point p1 = *((Point *)e1); Point p2 = *((Point *)e2); long double f1 = (p1.x-p0.x)* Distance(p2,p0); long double f2 = (p2.x-p0.x)* Distance(p1,p0); if (fabsl(f1-f2)<1e-5 ){ if (p1.x>p2.x)return 1 ; else if (p1.x<p2.x)return -1 ; else return 0 ; } else if (f1>f2)return -1 ; else if (f1<f2)return 1 ; } void GrahamScan (Point Q[],int n) { top = -1 ; p0 = (Point){MAX,MAX}; for (int i=0 ;i<n;i++){ if (Q[i].y<p0.y)p0 = Q[i]; else if (Q[i].y==p0.y){ if (Q[i].x<p0.x)p0=Q[i]; } } for (int i=0 ;i<n;i++){ if (Q[i].x==p0.x&&Q[i].y==p0.y){ for (;i<n-1 ;i++){ Q[i]=Q[i+1 ]; } } } qsort(Q,n-1 ,sizeof (Q[0 ]),cmp); stack [++top]=p0; stack [++top]=Q[0 ]; if (n - 1 < 2 )return ; stack [++top]=Q[1 ]; for (int i=2 ;i<n-1 ;i++){ while (Direction(stack [top-1 ],stack [top],Q[i])>=0 ){ top--; } stack [++top]=Q[i]; } }
旋转卡壳
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 long long area (Point p0, Point p1, Point p2) { return llabs((p1.x - p0.x) * (p2.y - p0.y) - (p1.y - p0.y) * (p2.x - p0.x)); } long long dis (Point p1, Point p2) { return (p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y); } long long rotatingCalipers () { int j = 2 ; stack [++top] = stack [0 ]; if (top < 3 ) { return dis(stack [0 ], stack [1 ]); } long long max = 0 ; for (int i = 0 ; i < top; i++) { while (area(stack [i], stack [i + 1 ], stack [j]) <= area(stack [i], stack [i + 1 ], stack [j % top + 1 ]))j = j % top + 1 ; long long tmp = max(dis(stack [i], stack [j]), dis(stack [i + 1 ], stack [j])); max = max(max, tmp); } return max; }
二维点对的查找
对所有点进行重复坐标的合并,并增加其点的个数
按照x升序,y再升序进行排列,查找某个点xy时,先查找x的左侧和右侧边界,然后在确定的边界内查找y
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 int binarySearchLeft (int x, int left, int right) { int saveRight = right; while (left <= right) { int mid = (left + right) / 2 ; if (s[mid].x < x)left = mid + 1 ; else right = mid - 1 ; } if (left > saveRight || s[left].x != x)return -1 ; else return left; } int binarySearchRight (int x, int left, int right) { int saveLeft = left; while (left <= right) { int mid = (left + right) / 2 ; if (s[mid].x > x)right = mid - 1 ; else left = mid + 1 ; } if (right < saveLeft || s[right].x != x)return -1 ; else return right; } int binarySearch (int x, int y, int left, int right) { int L = binarySearchLeft(x, left, right); int R = binarySearchRight(x, left, right); if (L == -1 || R == -1 )return 0 ; while (L <= R) { int mid = (L + R) / 2 ; if (s[mid].y < y)L = mid + 1 ; else if (s[mid].y > y)R = mid - 1 ; else return s[mid].cnt; } return 0 ; }
三角形三条边长度确定外接圆坐标
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 long long GP (long long x) { return x * x; } long double site[2 ];void getCirclePoint (Point A, Point B, Point C) { long double x = (long double ) ((GP(A.x) + GP(A.y)) * (B.y - C.y) + (GP(B.x) + GP(B.y)) * (C.y - A.y) + (GP(C.x) + GP(C.y)) * (A.y - B.y)) / (A.x * (B.y - C.y) + B.x * (C.y - A.y) + C.x * (A.y - B.y)) / 2 ; long double y = (long double ) ((GP(A.x) + GP(A.y)) * (C.x - B.x) + (GP(B.x) + GP(B.y)) * (A.x - C.x) + (GP(C.x) + GP(C.y)) * (B.x - A.x)) / (A.x * (B.y - C.y) + B.x * (C.y - A.y) + C.x * (A.y - B.y)) / 2 ; site[0 ] = x; site[1 ] = y; } long double calDisCircleToLine (Point cir[], Point line[]) { getCirclePoint(cir[0 ], cir[1 ], cir[2 ]); long double r = sqrtl((site[0 ] - cir[0 ].x) * (site[0 ] - cir[0 ].x) + (site[1 ] - cir[0 ].y) * (site[1 ] - cir[0 ].y)); long double dis = fabsl((site[0 ] - line[0 ].x) * (site[1 ] - line[1 ].y) - (site[1 ] - line[0 ].y) * (site[0 ] - line[1 ].x)) / distance(line[0 ], line[1 ]); if (dis > r)return dis - r; else return 0.00 ; }