计算几何入门
前言
前置知识:初中数学,数学必修一,必修二部分内容,三角函数。
文中提到的矩阵啥的不用管,没什么用
向量
向量是一个有大小和方向的量,可以用有箭头的线段表示。若向量的起点为
二位平面中充斥着无数的向量,通常为了方便表述一个向量,会将向量平移使得起点为原点,终点为
向量的模
向量的模长,即长度。向量
相反向量
方向相反,大小相等的向量为相反向量。向量
垂直向量
若向量
共线向量
若向量
零向量
零向量的模长为
向量的加法
若将向量看成一种移动的话,那么如上图。先按
向量的减法
可以将
向量的数乘
向量的数乘可记作
若
向量的点积
点积,又名数量积,点乘,点积的几何意义为
证明:
以上图为例,若
有
根据三角恒等变换有
分别将
得到
显然点积是有交换律的,因为
点积可用于判断向量之间的前后关系。
-
若两个向量共线且同向,那么它们的点积为他们的模长之积。
-
若
,则 。 -
若
,则 。 -
若
,则 。 -
若两个向量共线且反向,那么它们的点积为他们的模长之积的相反数。
如下图,直观的反应了夹角与点积的关系。
向量的叉积
又称外积,几何意义为向量根据平行四边形法则围成的面积。记为
证明:
仿照点积的证明,我们同样在
有
根据三角恒等变换有
分别将
得到
另一种更为直观的证明:
如上图,我们可以用用割补法将蓝色部分的面积求出。颜色相同的面积是相等的,这个用全等不难证出,那么
叉积是没有交换律的,因为
向量与矩阵之间有着密切的联系,叉积运算可看作是
叉积可用于判断向量之间的左右关系。
-
若两个向量共线,则
。 -
若
的终点在 的左侧,则 。 -
若
的终点在 的右侧,则 。
讲了这么多,向量的代码先放一下。
const double eps = 1e-8;//根据题目自选,通常这个好
int sgn(double x) { return fabs(x) < eps ? 0 : x > 0 ? 1 : -1; }//用于判断符号的
struct point {//向量,也可表示点
double x, y;
point(double x = 0, double y = 0) : x(x), y(y) {}
point operator + (const point &b) const { return point(x+b.x, y+b.y); }//加法
point operator - (const point &b) const { return point(x-b.x, y-b.y); }//减法
point operator * (const double &k) const { return point(x*k, y*k); }//数乘,乘法
point operator / (const double &k) const { return point(x/k, y/k); }//数乘,除法
};
double dot(point a, point b) { return a.x*b.x+a.y*b.y; }//点积
double crs(point a, point b) { return a.x*b.y-b.x*a.y; }//差积
double len(point a) { return sqrt(a.x*a.x+a.y*a.y); }//模长
线段交点
根据叉积我们可以求出线段的交点。
如上图,
先作
有
因为
所以
显然有
所以
所以
即可求出
特别地,若
代码:
struct line {
point s, e; double ang;//ang为线段与x轴的夹角
line() {}
line(point a, point b) { s = a, e = b, ang = atan2((b-a).y, (b-a).x); }
bool operator < (const line &b) const { return sgn(ang-b.ang) ? ang < b.ang : sgn(crs(b.s-s, b.e-s)) > 0; }//极角排序
};
point get(line a, line b) { double x = crs(b.s-a.s, b.e-a.s), y = crs(b.e-a.e, b.s-a.e); return a.s+(a.e-a.s)*x/(x+y); }//求线段的交点
向量旋转
如图,有一向量
记
根据三角恒等变换,有
将
代码:
point rotate(point a, double x) { return point(a.x*cos(x)-a.y*sin(x), a.y*cos(x)+a.x*sin(x)); }
三角剖分求面积
向量的叉积所求的面积是有向的,方向用正负表示。若给一个多边形,只要逆时针的把相邻两个点的叉积一次累加起来,就能得到这个多边形的面积,这是一个类似容斥的过程,下面举个例子,方便读者更好的体会这个过程。
对于多边形
接下来算
接下来算
接下来算
接下来算
最后算
代码:
...//逆时针输入一个n个点的多边形
for (int i = 1; i <= n; ++i) ans += crs(p[i], p[i == n ? 1 : i+1]);
printf("%.2lf", fabs(ans)/2);
凸包
想象一个平面有
首先这条绳子所围成的多边形一定为凸多边形,否则根据三角形不等式得出一定不是最优的,可以直接连边,如下图所示:
那么下面介绍两个算法:
Graham 算法
第一步我们需要找到一个纵坐标最小的点(如有相同的取横坐标最小的),然后极角排序,接下来从第一个点开始扫描每个点。过程中需要维护一个栈,也是当前我们凸包已经选的点,若当前点与栈顶的点的连线与上一条方向不符时,那么去掉栈顶的点,直到符合要求时加入栈。扫描完毕时栈中的点集即为凸包的点。
下面是图解:
若干点。
选出
栈中加入
栈中加入
栈中加入
栈中加入
符合要求。
栈中加入
栈中加入
符合要求。
栈中加入
最后算法结束,栈中为
代码:
#include <bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
#define mk make_pair
#define ll long long
#define space putchar(' ')
#define enter putchar('\n')
using namespace std;
typedef vector <int> vi;
typedef pair <int, int> pii;
inline int rd() { int x = 0, f = 1; char c = getchar(); while (!isdigit(c)) f = c == '-' ? -1 : f, c = getchar(); while (isdigit(c)) x = (x<<3)+(x<<1)+(c^48), c = getchar(); return x*f; }
inline ll rdll() { ll x = 0, f = 1; char c = getchar(); while (!isdigit(c)) f = c == '-' ? -1 : f, c = getchar(); while (isdigit(c)) x = (x<<3)+(x<<1)+(c^48), c = getchar(); return x*f; }
template <typename T> inline void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x/10); putchar(x%10+48); }
const double eps = 1e-8;
const int N = 1e5+5;
struct point {
double x, y;
point(double x = 0, double y = 0) : x(x), y(y) {}
point operator + (const point &b) const { return point(x+b.x, y+b.y); }
point operator - (const point &b) const { return point(x-b.x, y-b.y); }
point operator * (const double &k) const { return point(x*k, y*k); }
point operator / (const double &k) const { return point(x/k, y/k); }
} p[N], s[N];
double dot(point a, point b) { return a.x*b.x+a.y*b.y; }
double crs(point a, point b) { return a.x*b.y-b.x*a.y; }
int sgn(double x) { return fabs(x) < eps ? 0 : x > 0 ? 1 : -1; }
double len(point a) { return sqrt(a.x*a.x+a.y*a.y); }
double dist(point a, point b) { return len(a-b); }
bool cmp(point x, point y) { return sgn(crs(x-p[1],y-p[1])) > 0 ? 1 : sgn(crs(x-p[1], y-p[1])) < 0 ? 0 : dist(p[1], x) < dist(p[1], y); }
int main() {
int n = rd(), top = 0;
for (int i = 1; i <= n; ++i) {
scanf("%lf%lf", &p[i].x, &p[i].y);
if (i > 1 && (p[i].y < p[1].y || (p[i].y == p[1].y && p[i].x < p[1].x))) swap(p[1], p[i]);
}
sort(p+2, p+n+1, cmp);
s[++top] = p[1];
for (int i = 2; i <= n; ++i) {
while (top > 1 && sgn(crs(s[top]-s[top-1], p[i]-s[top])) <= 0) --top;
s[++top] = p[i];
}
s[++top] = p[1];
double ans = 0;
for (int i = 1; i < top; ++i) ans += dist(s[i], s[i+1]);
printf("%.2lf", ans);
return 0;
}
还在施工中 qwq……
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)