暑期训练·计算几何模板整理

是时候建立自己的计算几何模板了
计算几何精度问题

点和线

定义点

struct point
{
	double x, y;
	point() {}
	point(double x, double y) :x(x), y(y) {}
	point operator +(point B) { return point(B.x + x, B.y + y); }
	point operator -(point B) { return point(x - B.x, y - B.y); }
	point operator /(double B) { return point(x / B, y / B); }
	point operator *(double B) { return point(B * x, B * y); }
	//绕原点旋转角度B(弧度值),后x,y的变化
	void transXY(double B)
    {
        double tx = x,ty = y;
        x = tx*cos(B) - ty*sin(B);
        y = tx*sin(B) + ty*cos(B);
    }
}

定义线段

struct line
{
    point s,e;
    line(point _s,point _e)
    {
        s = _s;e = _e;
    }
};

判断线段相交(非规范)

bool inter(line l1,line l2)
{
    return 
        max(l1.s.x,l1.e.x) >= min(l2.s.x,l2.e.x) &&
        max(l2.s.x,l2.e.x) >= min(l1.s.x,l1.e.x) &&
        max(l1.s.y,l1.e.y) >= min(l2.s.y,l2.e.y) &&
        max(l2.s.y,l2.e.y) >= min(l1.s.y,l1.e.y) &&
        sgn((l2.s-l1.s)^(l1.e-l1.s))*sgn((l2.e-l1.s)^(l1.e-l1.s)) <= 0 &&
        sgn((l1.s-l2.s)^(l2.e-l2.s))*sgn((l1.e-l2.s)^(l2.e-l2.s)) <= 0;
}

规范相交
《算法竞赛入门到进阶》P280

定义有向线段

struct line
{
	point p;
	vec v;
	double ang;
	line(point p1, vec v1) :p(p1), v(v1) { ang = atan2(v.y, v.x); }
	bool operator <(line L) { return ang < L.ang; }//极角排序
};
//atan2(y,x),x可以为0

点乘

double Dot(vec A, vec B) { return A.x*B.x + A.y*B.y; }

矢量长度

double lec(vec A) { return sqrt(Dot(A, A)); }
double lec2(vec A) { return (Dot(A, A)); }

叉乘

double cross(vec A, vec B) { return A.x*B.y - A.y*B.x; }

点线距、点在线上投影、点关于线的对称点等,见《算法竞赛入门到进阶》P277-280

两条线整数交点
参考博客

POJ 3304
寻找和所有线段相交的直线

线段与多边形的关系

圆:

见《算法竞赛入门到进阶》P294-295

判断圆与多边形的关系:参考博客
(同时也可以判断点与多边形的关系)

最近点对

参考博客
(分治法)

凸包-Graham-Scan算法

参考博客

旋转卡壳详解参考博客

求最远点对(凸包+旋转卡壳)

#include 
#include 
#include 
#include 
using namespace std;
const int MAXN = 50010;
typedef long long ll;
const double eps = 1e-8;
struct Point {
	double x, y;
};
Point list[MAXN];
int stack[MAXN], top;
/***
* 叉积
* a×b>0, 则b在a的逆时针方向;
* a×b<0, 则b在a的顺时针方向;
* a×b=0, 则a与b共线,但可能同向也可能反向。
*/
double crossProduct(Point a, Point b) {
	return a.x*b.y - a.y*b.x;
}

int sgn(double x) {
	if (fabs(x) < eps) return 0;
	if (x < 0) return -1;
	else return 1;
}

Point sub(Point a, Point b) {
	Point p;
	p.x = a.x - b.x;
	p.y = a.y - b.y;
	return p;
}
double dist(Point a, Point b) {
	return (a.x - b.x)*(a.x - b.x) + (a.y - b.y)*(a.y - b.y);
}
//相对于极点list[0]的极角排序
bool cmp(Point p1, Point p2) {
	double temp = crossProduct(sub(p1, list[0]), sub(p2, list[0]));
	if (sgn(temp) > 0) return true;
	else if (sgn(temp) == 0 && sgn(dist(p1, list[0]) - dist(p2, list[0])) <= 0) return true;
	else return false;
}
/*
* 求凸包,Graham算法
* 点的编号0~n-1
* 返回凸包结果Stack[0~top-1]为凸包的编号
*/
void Graham(int n) 
{
	Point p0 = list[0];
	int k = 0;
	for (int i = 1; i < n; i++) 
	{
		if (p0.y > list[i].y || (p0.y == list[i].y && p0.x > list[i].x)) 
		{
			p0 = list[i];
			k = i;
		}
	}
	swap(list[k], list[0]);
	sort(list + 1, list + n, cmp);

	stack[0] = 0;
	if (n == 1) { top = 1; return; }
	stack[1] = 1;
	if (n == 2) { top = 2; return; }

	top = 2;
	for (int i = 2; i < n; i++) 
	{
		while (top > 1 && sgn(crossProduct(sub(list[stack[top - 1]], list[stack[top - 2]]), sub(list[i], list[stack[top - 2]]))) <= 0) 
		{
			top--;
		}
		stack[top++] = i;
	}
}
double RC(int top)//旋转卡壳
{
	double ans = 0; 
	int now = 2;
	for (int i = 0; i < top; i++)
	{
		while (crossProduct(sub(list[stack[i + 1]], list[stack[i]]), sub(list[stack[now]], list[stack[i]])) < crossProduct(sub(list[stack[i + 1]], list[stack[i]]), sub(list[stack[(now + 1) % top]], list[stack[i]])))
		{
			now++; //最远距离对应了最大面积。
			if (now == top) now = 0;
		}
		ans = max(ans, dist(list[stack[now]], list[stack[i]]));
	}
	return ans;
}

int main()
{
	int N;
	scanf_s("%d", &N);
	for (int i = 0; i < N; i++)
		scanf_s("%lf%lf", &list[i].x, &list[i].y);
	Graham(N);
	printf("%lld", (ll)RC(top));
	return 0;
}

某巨佬的旋转卡壳专栏

旋转卡壳动图

求空间散点组成的最大三角形面积
对n个点,分别固定他们,然后用旋转卡壳找另两个点(因为三角形的一个点对于对应的边一定是对踵点)

参考博客

计算几何中的最短路问题

POJ 1556

POJ 2966

半平面交

HPI() 模板参考《算法竞赛入门到进阶》 P292

HDU 4316
凸包+半平面交
get知识:求多面体在某一平面的投影,只需把顶点的投影计算出来,然后求凸包

偶然看到的向量模板参考博客

HDU 5572

原题地址

代码:
自闭了一下午,调出来了(现场赛这个手速,要被队友拉去煲汤)

#include 
#include 
#include 
#include 
using namespace std;
const int MAXN = 110;
typedef long long ll;
const double eps = 1e-8;
int sgn(double x)
{
	if (fabs(x) < eps) return 0;
	return x < 0 ? -1 : 1;
}
struct point
{
	double x, y;
	point() {}
	point(double x, double y) :x(x), y(y) {}
	point operator +(point B) { return point(B.x + x, B.y + y); }
	point operator -(point B) { return point(x - B.x, y - B.y); }
	point operator /(double B) { return point(x / B, y / B); }
	point operator *(double B) { return point(B * x, B * y); }
}pb, pa;
typedef point vec;
double Dot(vec A, vec B) { return A.x*B.x + A.y*B.y; }
double lec(vec A) { return sqrt(Dot(A, A)); }
double lec2(vec A) { return (Dot(A, A)); }
double cross(vec A, vec B) { return A.x*B.y - A.y*B.x; }
double dist(point A, point B) { return sqrt((A.x - B.x)*(A.x - B.x) + (A.y - B.y)*(A.y - B.y)); }
struct line
{
	point p;
	vec v;
	double ang;
	//line(point p1, point p2) :p(p1), v(p2 - p1) { ang = atan2(p2.y - p1.y, p2.x - p1.x); }
	line(point p1, vec v1) :p(p1), v(v1) { ang = atan2(v.y, v.x); }
	bool operator <(line L) { return ang < L.ang; }
};
struct cir
{
	double x, y;
	double r;
	point o;
	cir(double x, double y, double r) :x(x), y(y), r(r) { o = point(x, y); }
};
point p_l_proj(point p, line l)//投影
{
	double k = Dot(l.v, p - l.p) / lec2(l.v);
	return l.p + l.v*k;
}
bool p_o_l(point p, line l)//点在线上
{
	//vec ww(p.x - l.p.x, p.y - l.p.y);
	if (sgn(cross(p - l.p, l.v))==0 && Dot(p - l.p, l.v) > 0) return true;
	return false;
}
double d_p_l(point p, line l)//点线距
{
	point p1 = l.p + l.v;
	return fabs(cross(p - l.p, l.v) / lec(l.v));
}
int l_c_r(line l, cir cr)//圆与线关系
{
	double dst = d_p_l(cr.o, l);
	if (Dot(cr.o - l.p, l.v) <= 0) return 0;//钝角逃逸
	if (sgn(dst - cr.r) >= 0) return 0;
	//else if (sgn(dst - cr.r) == 0) return 1;
	return 2;
}
int l_c_c(line l, cir cr)//圆线交点
{
	if (!l_c_r(l, cr)) return 0;
	point p = p_l_proj(cr.o, l);
	double d = d_p_l(cr.o, l);
	double k = sqrt(cr.r*cr.r - d * d);
	point n = (l.v) / lec(l.v);
	pb = p - n * k;
	pa = p + n * k;
	return 1;
}
point p_l_sym(point p, line l)//对称点
{
	point q = p_l_proj(p, l);
	return point(2 * q.x - p.x, 2 * q.y - p.y);
}
int T;
int main()
{
	ios::sync_with_stdio(false);
	int cas = 0, i;
	double ax, bx, r, ox, oy, ay, by, vx, vy;
	cin >> T;
	while (T--)
	{
		++cas;
		cin >> ox >> oy >> r;
		cir cr(ox, oy, r);
		cin >> ax >> ay >> vx >> vy;
		cin >> bx >> by;
		point A(ax, ay), B(bx, by);
		point va(vx, vy);
		line l(A, va);
		if (p_o_l(B, l))
		{
			if (l_c_r(l, cr)) //cout << "Case #" << cas << ": " << "No" << endl;
			{
/*
智障了,之前这里没考虑
0 0 1
0 11 0 -1
0 4
这样的数据,自闭了一下午
*/
				l_c_c(l, cr);
				point pans;
				if (dist(A, pa) > dist(A, pb)) pans = pb;
				if (dist(pans, A) >= dist(A,B))
				{
					cout << "Case #" << cas << ": " << "Yes" << endl;
				}
				else cout << "Case #" << cas << ": " << "No" << endl;
			}
			else cout << "Case #" << cas << ": " << "Yes" << endl;
			continue;
		}
		int w = l_c_c(l, cr);
		if (!w)
		{
			cout << "Case #" << cas << ": " << "No" << endl;
			continue;
		}
		point pans;
		if (dist(A, pa) > dist(A, pb)) pans = pb;
		else pans = pa;
		vec vk(pans.x - cr.x, pans.y - cr.y);
		vec mk(vk.y, -vk.x);//圆的切线
		if ((Dot(l.v, mk) < 0)) mk = point(-mk.x, -mk.y);//貌似这行是多余的
		line mir(pans, mk);
		point a2 = p_l_sym(A, mir);
		line l2 = line(a2, pans - a2);
		if (p_o_l(B, l2) && Dot(B - pans, pans - a2) >= 0)
		{
			cout << "Case #" << cas << ": " << "Yes" << endl;
		}
		else
		{
			cout << "Case #" << cas << ": " << "No" << endl;
		}
	}
	return 0;
}

貌似有大佬说要开long double,否则精度不够,不知道是不是数据被削弱了参考博客

目前只整理了二维计算几何,三维计算几何这种不属于阳间的东西 以后再补

你可能感兴趣的:(暑期训练·计算几何模板整理)