1 前言
1.1 生产生活
随着科学技术的飞速发展及计算机在国民经济各个领域中的普遍运用,计算机辅助设计,即CAD越来越为人们所重视。当前的CAD工作中,计算机远远不只是一种高效的计算工具,它已成为人们进行创造性设计活动的得力助手甚至参谋。计算几何作为CAD的基础理论之一,主要研究内容是几何形体的数学描述和计算机表述;它同计算机辅助几何设计,即CAGD有着十分密切的关系。而CAGD是由微分几何、代数几何、数值计算、逼近论、拓扑学以及数控技术等形成的一门新兴边缘学科,其主要研究对象和内容是对自由形曲线、曲面的数学描述、设计、分析及图形的显示、处理等。
1.2 ICPC竞赛
-
在竞赛中,计算几何相比于其他部分来说是比较独立的
-
曾出现过其与图论和动态规划相结合的题目,计算几何与其他的知识点很少有过多的结合
-
计算几何的题目难度不会很大,但也永远不会成为最水的题
-
计算几何题目具有代码量大、特殊情况多。精度问题难以控制等特点
2 准备知识
2.1 头文件及函数及常量
#include <cstdio>
#include <cmath>
using namespace std;
const double pi = acos(-1.0);
const double inf = 1e100;
const double eps = 1e-6;
int sgn(double d){
if(fabs(d) < eps)
return 0;
if(d > 0)
return 1;
return -1;
}
int main() {
double x = 1.49999;
int fx = floor(x);//向下取整函数
int cx = ceil(x);//向上取整函数
int rx = round(x);//四舍五入函数
printf("%f %d %d %d\n", x, fx, cx, rx);
//输出结果 1.499990 1 2 1
return 0 ;
}
2.2 浮点误差
计算几何中一般来说使用 d o u b l e double double类型比较频繁,该用实数是就用 d o u b l e double double, f l o a t float float容易失去精度
d o u b l e double double类型读入时应用 % l f \%lf %lf占位符
而输出时应用 % f \%f %f占位符
2.2.1 计算误差
尽量少用三角函数、除法、开方、求幂、取对数运算
-
尽量把公式整理成使用的以上操作最少的形式
-
1.0 / 2.0 ∗ 3.0 / 4.0 ∗ 5.0 = ( 1.0 ∗ 3.0 ∗ 5.0 ) / ( 2.0 ∗ 4.0 ) 1.0/2.0*3.0/4.0*5.0 = (1.0*3.0*5.0)/(2.0*4.0) 1.0/2.0∗3.0/4.0∗5.0=(1.0∗3.0∗5.0)/(2.0∗4.0)
-
在不溢出的情况下将除式比较转化为乘式比较
-
a b > c ⇔ a > b c \frac {a}{b} > c \Leftrightarrow a > bc ba>c⇔a>bc
在使用除法、开根号、和三角函数的时候,我们要考虑由浮点误差运算产生的代价
2.2.2 判等
不要直接用等号判断浮点数是否相等!
不要直接用等号判断浮点数是否相等!
不要直接用等号判断浮点数是否相等!
同样的一个数 1.5 1.5 1.5如果从不同的途径计算得来,它们的储存方式可能会是 a = 1.4999999 a = 1.4999999 a=1.4999999 与 b = 1.5000001 b = 1.5000001 b=1.5000001,此时使用 a = = b a == b a==b判断将返回 f a l s e false false
2.2.2.1 解决方案 1 误差判别法
借助 ε \varepsilon ε也就是上面定义的常量eps
const double eps = 1e-9;
int dcmp(double x, double y){
if(fabs(x - y) < eps)
return 0;
if(x > y)
return 1;
return -1;
}
2.2.2.2 解决方案 2 化浮为整
在不溢出整数范围的情况下,可以通过乘上 1 0 k 10 ^ k 10k转化为整数运算,最后再将结果转化为浮点数输出
2.2.3 负零
输出时一定要小心不要输出 − 0 -0 −0,比如
double a = -0.000001;
printf("%.4f", a);
2.2.4 反三角函数
使用反三角函数时,要注意定义域的范围,比如,经过计算 x = 1.000001
double x = 1.000001;
double acx = acos(x);
//可能会返回runtime error
//此时我们可以加一句判断
double x = 1.000001;
if(fabs(x - 1.0) < eps || fabs(x + 1.0) < eps)
x = round(x);
double acx = acos(x);
2.3 模板总结
#include <cstdio>
#include <cmath>
using namespace std;
const double pi = acos(-1.0);
const double inf = 1e100;
const double eps = 1e-6;
int sgn(double d){
if(fabs(d) < eps)
return 0;
if(d > 0)
return 1;
return -1;
}
int dcmp(double x, double y){
if(fabs(x - y) < eps)
return 0;
if(x > y)
return 1;
return -1;
}
int main() {
double x = 1.49999;
int fx = floor(x);//向下取整函数
int cx = ceil(x);//向上取整函数
int rx = round(x);//四舍五入函数
printf("%f %d %d %d\n", x, fx, cx, rx);
//输出结果 1.499990 1 2 1
return 0 ;
}
3 点与向量
3.2 相关定义
3.1.1 点的定义
二维平面下的点的表示只需要两个实数,即横坐标与纵坐标即可
struct Point{
double x, y;
Point(double x = 0, double y = 0):x(x),y(y){
}
};
3.1.2 向量的定义
- 既有大小又有方向的量叫做向量
- 在计算机中我们常用坐标表示
这样看来,向量这个结构体貌似与点没有任何区别,因此我们可以
typedef Point Vector;
3.2 运算
3.2.1 加减乘除
3.2.1.1 加法运算
-
点与点之间的加法运算没有意义
-
点与向量相加得到另一个点
-
向量与向量相加得到另外一个向量
Vector operator + (Vector A, Vector B){
return Vector(A.x+B.x, A.y+B.y);
}
3.2.1.2 减法运算
两个点之间作差将得到一个向量, A − B A - B A−B将得到由 B B B指向 A A A的向量 B A ⃗ \vec{BA} BA
Vector operator - (Point A, Point B){
return Vector(A.x-B.x, A.y-B.y);
}
3.2.1.3 乘法运算
向量与实数相乘得到等比例缩放的向量
Vector operator * (Vector A, double p){
return Vector(A.x*p, A.y*p);
}
3.2.1.4 除法运算
向量与实数相除得到等比例缩放的向量
Vector operator / (Vector A, double p