ACM 计算几何基础模板

在ACM计算几何中,我们常常重复用到许多方法,例如求点距,求直线方程,等等。不妨利用模板总结之。这些模板为我从网上学习后加入自己的理解糅合得来qwq,看了很多的博客,查了很多书籍,其中印象最深的是林夕林夕关于计算几何模板的总结,大家也可以去参考他的应该比我的更详细qwq
(不过大佬最近好像出没比较少了)
所以我这个主要还是留着自己参考吧,能帮到他人再好不过了

为了不产生歧义,方便理解,本模板使用笛卡尔坐标系,封装的多是数学中常见的函数。

前置知识:高中数学,向量外积相关

模板仅供参考学习,不保证任何情况下不会因为误差WA掉或是RE掉

基础声明

首先,打acm的时候double这个数据类型就比较恶心(容易浮动导致误差),而计算几何中各种计算又几乎没法单独用整型解决。因此我们需要一个小常数来判断误差。

例如,计算啥啥面积的时候,我们需求误差在10的-6次方以内,那么就可以定义这个常数为10的-6次方:

const double eps = 1e-6;

然后,我们的目标是,当误差小于1e-6时,误差将被忽略不计。也就是说,两个数比较时,差距在1e-6以内,我们就可以认为这两个数相等。

一般的计算中,本来两个相等的数字,因为我们计算路径的不同,浮点数就可能会发生不同的浮动,导致 = = == ==运算符检查本来应该相等的两个浮点数时返回了0。这时我们就可以用误差来避免了。

以下为重载大小判断的函数:

int cmp(const double& a, const double& b)
{
	if(fabs(a - b) < eps) return 0; // 若误差范围内相等,返回0
	else if(a > b) return 1; // 若a > b,返回1
	return -1;	// 若a < b,返回-1
}

另外还有计算几何中常用的常量:

const double pi = 3.1415927536;
const double INF = 1e100;

浮点数的表数范围巨大,可以表示到10的200多次方,但是实际上几十次方浮动误差就大到难以接受了……因此,我们可以用一个100次方表示无穷大,反正它够大就对了,但是却不能直接拿来做计算(总之就是误差非常大)。

另外,我们也常用C++内置的反三角函数acos()来求角度。但是这个函数有个问题,我们都知道反余弦函数的定义域是 [ − 1 , 1 ] [-1,1] [1,1],然而由于浮点数的浮动偏移,我们得到的1很可能实际上是1.00001,1.0000001这种数,这些数据不在反余弦函数的定义域内了,那么把它们作为参数传进去就没法计算,就会导致Runtime Error,就没法AC!QAQ!所以我们可以把acos()函数二次包装一下,打消这个顾虑。

double acs(double x)
{
	if(x > 1) x = 1;
	else if(x < -1) x = -1;
	return acos(x);
}

顺便把反正弦函数也包装一下

double asn(double x)
{
	if(x > 1) x = 1;
	else if(x < -1) x = -1;
	return asin(x);
}

当然啦,上面这个函数的调用还得保证理论上传入的x确实在 [ − 1 , 1 ] [-1,1] [1,1]范围内。

另外还有一个关于double注意事项,即其应该以%lf格式读入,而以%f格式输出。寒假曾因为这个在好几个图论题上wa了,自己也不知道为啥,后来也有很多巨巨强调……

好,这些就是基本需要注意的地方,下面我们来建立笛卡尔的王国吧~

直接定义:

struct point
{
	double x, y;
	point(double xx = 0, double yy = 0){x = xx, y = yy;}
};

即可。
然后点与点之间有一系列的计算函数,下面列出我用过的几个:

double dis(const point& a, const point& b) // 计算两点距离
{
	return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}

double slope(const point& a, const point& b) // 求两点斜率
{
	if(cmp(a.x, b.x) == 0) return INF;
	return (a.y - b.y) / (a.x - b.x);
}

double gradient(const point& a, const point& b) // 计算倾斜角
{
	if(cmp(a.y, b.y) == 0) return 0;
	if(cmp(a.x, b.x) == 0) return pi / 2.0;
	return atan(slope(a, b));
}

只有点的就到这里,下面的向量是应用更多的~

向量

由于空间中的向量也可以用坐标表示,因此我们不妨:

typedef point Vector;

V大写是因为关键字vector已经被STL用去了。(C无视我)

在计算几何领域,使用向量不仅操作比传统解析几何方法更简洁易写,而且产生的浮点误差也更小。因此,向量是计算几何的重头戏。

先看看高中学过的一些东西:
向量点积,重载一下:

double operator * (const Vector& a, const Vector& b)
{
	return a.x * b.x + a.y * b.y;
}

接着是几大运算,向量加减,数乘等等等等

Vector operator + (const Vector& a, const Vector& b) // 向量加法
{
	return Vector(a.x + b.x, a.y + b.y);
}

Vector operator - (const Vector& a, const Vector& b) // 向量减法
{
	return Vector(a.x - b.x, a.y - b.y);
}

Vector operator * (const Vector& a, const double& b) // 向量数乘
{
	return Vector(a.x * b, a.y * b);
}

double length(const Vector& x) // 向量模长
{
	return sqrt(x * x);
}

Vector angle(const Vector& a, const Vector& b) // 向量夹角
{
	return acs((a * b) / length(a) / length(b));
}

向量叉乘,返回结果向量的有向长度。用^运算符重载之。
注意叉乘的绝对值也就是两向量围成的平行四边形的面积。

double operator ^ (const Vector& a, const Vector& b)
{
	return a.x * b.y - a.y * b.x;
}

对向量进行操作。向量逆时针旋转:

Vector Rotate(const Vector& a, double rad)
{
	return Vector(A.x * cos(rad) - A.y * sin(rad), A.x * sin(rad) + A.y * cos(rad));
}

接下来,点的排序。在诸如求凸包的一类问题中,我们需要为点进行排序方便操作,这里以Andrew凸包算法中的Left then Low排序为例给一个排序函数:

bool leftThenLow(const point& a, const point& b)
{
	if(cmp(a.x, b.x) == 0) return a.y < b.y;
	return a.x < b.x;
}

以及Graham Scan中必不可少的极角排序:

point p; // 基准点
bool angleSort(const point& a, const point& b)
{
	return (angle(a - p, Vecotr(1, 0)) < angle(b - p, Vector(1, 0)));
}

直线

计算几何体系的直线通常使用向量表示法(类似参数方程)而不是一般的 y = k x + b y=kx+b y=kx+b的形式。一般我们这样表示直线: y = v t + p y=vt+p y=vt+p. 其中 v v v是一个向量,而 p p p是某个点。其几何意义为从一定点 p p p出发,经过 t t t v v v所能到达的点的集合。这样的表示方法是因为可以表示无斜率的直线和斜率为0的直线,也能表示线段。有了这个表示法,我们再进行一系列函数封装。

struct line
{
	double v, p;
	line(double vv = 0, double pp = 0){v = vv, p = pp;}
};

点线距离:

double dis(const line& l, const point& x)
{
	Vector v = l.p - x;
	return fabs(v ^ l.v) / length(l.v);
}

点是否在线上:

bool pointOnLine(const point& x, const line& l)
{
	return cmp((Vector(l.p - x) ^ l.v), 0);
}

点在直线上的投影:

point prj(const point& x, const line& l)
{
	return l.p + l.v * (l.v * (x - l.p) / (l.v * l.v));
}

使用标准方法表示:圆心和半径。

struct circle
{
	point p;
	double r;
	circle(double x, double y, double rr){p.x = x, p.y = y, r = rr;}
};
  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
ACM 很全的计算几何模板 基础部分 1.几何公式 5 1.1三角形 5 1.2四边形 5 1.3正n边形 5 1.4圆 5 1.5棱柱 6 1.6棱锥 6 1.7棱台 6 1.8圆柱 6 1.9圆锥 6 1.10圆台 7 1.11球 7 1.12球台 7 1.13球扇形 7 2.直线与线段 7 2.0预备函数 7 2.1判三点是否共线 8 2.2判点是否在线段上 9 2.3判断两点在线段的同一侧 9 2.4判断两点是否在线段的异侧 9 2.5求点关于直线的对称点 10 2.7判断两线段是否相交 10 2.7.1常用版 10 2.7.2不常用版 11 2.8 求两条直线的交点 11 2.9点到直线的最近距离 12 2.10点到线段的最近距离 12 3.多边形 12 3.0 预备浮点函数 12 3.1判定是否是凸多边形 13 3.2判定点是否在多边形内 14 3.3 判定一条线段是否在一个任意多边形内 15 4. 三角形 16 4.0预备函数 16 4.1求三角形的外心 17 4.2求三角形内心 17 4.3求三角形垂心 17 5. 圆 18 5.0预备函数 18 5.1判定直线是否与圆相交 19 5.2判定线段与圆相交 19 5.3判圆和圆相交 19 5.4计算圆上到点p最近点 19 5.5计算直线与圆的交点 20 5.6计算两个圆的交点 20 6. 球面 21 6.0给出地球经度纬度,计算圆心角 21 6.1已知经纬度,计算地球上两点直线距离 21 6.2已知经纬度,计算地球上两点球面距离 21 7. 三维几何的若干模板 22 7.0预备函数 22 7.1判定三点是否共线 23 7.2判定四点是否共面 23 7.1判定点是否在线段上 23 7.2判断点是否在空间三角形上 24 7.3判断两点是否在线段同侧 24 7.4判断两点是否在线段异侧 25 7.5判断两点是否在平面同侧 25 7.6判断两点是否在平面异侧 25 7.7判断两空间直线是否平行 25 7.8判断两平面是否平行 26 7.9判断直线是否与平面平行 26 7.10判断两直线是否垂直 26 7.11判断两平面是否垂直 26 7.12判断两条空间线段是否相交 27 7.13判断线段是否与空间三角形相交 27 7.14计算两条直线的交点 28 7.15计算直线与平面的交点 28 7.16计算两平面的交线 29 7.17点到直线的距离 29 7.18 计算点到平面的距离 29 7.19计算直线到直线的距离 30 7.20空间两直线夹角的cos值 30 7.21两平面夹角的cos值 30 7.22直线与平面夹角sin值 31 1.最远曼哈顿距离 31 2. 最近点对 32 3. 最近点对 34 4. 最小包围圆 36 5. 求两个圆的交点 39 6. 求三角形外接圆圆心 40 7. 求凸包 42 8.凸包卡壳旋转求出所有对踵点、最远点对 44 9. 凸包+旋转卡壳求平面面积最大三角 47 10. Pick定理 50 11. 求多边形面积和重心 51 12. 判断一个简单多边形是否有核 52 13. 模拟退火 54 14. 六边形坐标系 56 15. 用一个给定半径的圆覆盖最多的点 60 16. 不等大的圆的圆弧表示 62 17. 矩形面积并 62 18. 矩形的周长并 66 19. 最近圆对 70 20. 求两个圆的面积交 74

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值