33 计算几何学

1 线段的性质

1.1 一些定义

Pasted image 20241109175331.png
Pasted image 20241109175516.png

1.2 确定连续线段是向左转还是向右转

已知
计算

  • 结果 ,则在 左转
  • 结果 ,则在 右转
  • 结果 ,则在 三者共线

1.3 判定两条线是否相交

伪代码

Pasted image 20241109175946.png
Pasted image 20241109180014.png

cpp实现

struct dot{  
    int x, y;  
};  
  
int direction(dot pi, dot pj, dot pk){  
    return ((pk.x-pi.x)*(pj.y-pi.y)-(pk.y-pi.y)*(pj.x-pi.x));  
}  
bool onSegment(dot pi, dot pj, dot pk){  
    if(min(pi.x, pj.x) <= pk.x && max(pi.x, pj.x) >= pk.x &&  
            min(pi.y, pj.y) <= pk.y && max(pi.y, pj.y) >= pk.y){  
        return true;  
    }  
    return false;  
}  
/*  
 * 线段p1p2与p3p4是否相交  
 */bool segmentsIntersect(dot p1, dot p2, dot p3, dot p4){  
    int d1 = direction(p3, p4, p1);  
    int d2 = direction(p3, p4, p2);  
    int d3 = direction(p1, p2, p3);  
    int d4 = direction(p1, p2, p4);  
    if(((d1>0 && d2<0)||(d1<0 && d2>0)) && ((d3>0 && d4<0)||(d3<0 && d4>0))){  
        return true;  
    }else if(d1 == 0 && onSegment(p3, p4, p1)){  
        return true;  
    }else if(d2 == 0 && onSegment(p3, p4, p2)){  
        return true;  
    }else if(d3 == 0 && onSegment(p1, p2, p3)){  
        return true;  
    }else if(d4 == 0 && onSegment(p1, p2, p4)){  
        return true;  
    }else{  
        return false;  
    }  
}

2 确定任意一对线段是否相交

O(nlgn)的红黑树(未实现)

  • 简化
    Pasted image 20241109225042.png
  • 线段排序
    Pasted image 20241109225059.png
  • 移动扫除线
    Pasted image 20241109225129.png
    Pasted image 20241109225141.png
  • 求线段交点的伪代码
    Pasted image 20241109225205.png
    Pasted image 20241109225217.png

简单易懂的O(n^2)的cpp实现

#include <iostream>  
#include <algorithm>  
#include <vector>  
using namespace std;  
  
struct Point {  
    int x, y;  
	
    Point operator+(const Point &b) const {  
        return {x + b.x, y + b.y};  
    }  
	
    Point operator-(const Point &b) const {  
        return {x - b.x, y - b.y};  
    }  
	
    Point operator*(const int &b) const {  
        return {x * b, y * b};  
    }  
    
    int operator^(const Point &b) const {  
        return x * b.y - y * b.x;  
    }  
};  
  
struct Line {  
    Point p;  
    Point q;  
};  
  
vector<Line> lines;  
  
int sgn(int x);  
bool intersect(Line l1, Line l2);  
bool onSegment(Point point, Line line);  
  
int main() {  
    int n, cnt = 0;  
    cin >> n;  
    int x1, y1, x2, y2;  
    for (int i = 0; i < n; i++) {  
        cin >> x1 >> y1 >> x2 >> y2;  
        lines.push_back({{x1, y1}, {x2, y2}});  
    }  
    for (int i = 0; i < n; i++)  
        for (int j = 0; j < i; j++)  
            if (intersect(lines[i], lines[j]))  
                cnt++;  
    cout << cnt;  
    return 0;  
}  
  
int sgn(int x) {  
    if (x < 0) return -1;  
    if (x > 0) return 1;  
    return 0;  
}  
  
bool intersect(Line l1, Line l2) {  
    int d1 = sgn((l1.q - l1.p) ^ (l2.p - l1.p));  
    int d2 = sgn((l1.q - l1.p) ^ (l2.q - l1.p));  
    int d3 = sgn((l2.q - l2.p) ^ (l1.p - l2.p));  
    int d4 = sgn((l2.q - l2.p) ^ (l1.q - l2.p));  
    if (d1 * d2 < 0 && d3 * d4 < 0)  
        return true;  
    if (d1 == 0 && onSegment(l2.p, l1))  
        return true;  
    if (d2 == 0 && onSegment(l2.q, l1))  
        return true;  
    if (d3 == 0 && onSegment(l1.p, l2))  
        return true;  
    if (d4 == 0 && onSegment(l1.q, l2))  
        return true;  
    return false;  
}  
  
bool onSegment(Point point, Line line) {  
    if (point.x >= min(line.p.x, line.q.x) &&  
        point.x <= max(line.p.x, line.q.x) &&  
        point.y >= min(line.p.y, line.q.y) &&  
        point.y <= max(line.p.y, line.q.y))  
        return true;  
    return false;  
}

3 寻找凸包

Pasted image 20241110171727.png
Pasted image 20241110171738.png

Graham扫描法

Pasted image 20241110171811.png
Pasted image 20241110171820.png
Pasted image 20241110171828.png

Jarvis步进法

Pasted image 20241110171942.png

Graham扫描法 cpp实现

#include <iostream>  
#include <algorithm>  
#include <cmath>  
#include <cstdio>  
  
using namespace std;  
  
const int MAX = 200005;  
const double eps = 1e-7;  
  
struct Point {  
double x, y;  
  
Point operator+(const Point &b) const {  
return {x + b.x, y + b.y};  
}  
  
Point operator-(const Point &b) const {  
return {x - b.x, y - b.y};  
}  
  
double operator^(const Point &b) const {  
return x * b.y - y * b.x;  
}  
  
bool operator<(const Point &b) const {  
if (x != b.x)  
return x < b.x;  
return y < b.y;  
}  
};  
  
Point p[MAX];  
Point s[MAX];  
int top;  
  
void selMin(int n);  
int cmp(Point a, Point b);  
bool equal(double a, double b);  
double dis(Point a, Point b);  
void graham(int n);  
double s_sqr(Point a, Point b, Point c);  
double diameter();  
  
int main() {  
	int n;  
	cin >> n;  
	for (int i = 0; i < n; i++)  
		cin >> p[i].x >> p[i].y;  
	selMin(n);  
	sort(p + 1, p + n, cmp);  
	graham(n);  
	printf("%.6f", sqrt(diameter())) ;  
	return 0;  
}  
  
void selMin(int n) {  
	Point Min = p[0];  
	int IDMin = 0;  
	for (int i = 0; i < n; i++)  
		if (p[i] < Min) {  
			Min = p[i];  
			IDMin = i;  
		}  
	swap(p[0], p[IDMin]);  
}  
  
int cmp(Point a, Point b) {  
	double x = (a - p[0]) ^ (b - p[0]);  
	if (x > 0)  
		return 1;  
	if (equal(x, 0) && (dis(a, p[0]) < dis(b, p[0])))  
		return 1;  
	return 0;  
}  
  
double dis(Point a, Point b) {  
	double x = a.x - b.x;  
	double y = a.y - b.y;  
	return x * x + y * y;  
}  
  
void graham(int n) {  
	top = 1;  
	s[0] = p[0];  
	s[1] = p[1];  
	for (int i = 2; i < n; i++) {  
		while (top > 1 && ((p[i] - s[top]) ^ (s[top - 1] - s[top])) <= 0)  
			top--;  
		s[++top] = p[i];  
	}  
}  
  
double s_sqr(Point a, Point b, Point c) {  
	return fabs((a - b) ^ (c - b));  
}  
  
double diameter() {  
	double diam = 0;  
	int j = 2;  
	s[++top] = s[0];  
	if (top < 3)  
	return dis(s[0], s[1]);  
	for (int i = 0; i < top - 1; i++) {  
		while (s_sqr(s[i], s[i + 1], s[j]) < s_sqr(s[i], s[i + 1], s[(j + 1) % top])) 
			j = (j + 1) % top;  
		diam = max(diam, max(dis(s[i], s[j]), dis(s[i + 1], s[j])));  
	}  
	return diam;  
}  
bool equal(double a, double b){  
	return fabs(a - b) < eps;  
}

4 其他会用到的公式

f949fbcfc439a6e846be9031db2d5f5.png

Built with MDFriday ❤️