1. 前置知识点
(1) pi = acos(-1);
(2) 余弦定理 c^2 = a^2 + b^2 - 2abcos(t)
2. 浮点数的比较
const double eps = 1e-8;
int sign(double x) // 符号函数
{
if (fabs(x) < eps) return 0;
if (x < 0) return -1;
return 1;
}
int cmp(double x, double y) // 比较函数
{
if (fabs(x - y) < eps) return 0;
if (x < y) return -1;
return 1;
}
3. 向量
3.1 向量的加减法和数乘运算
3.2 内积(点积) A·B = |A||B|cos(C)
(1) 几何意义:向量A在向量B上的投影与B的长度的乘积。
(2) 代码实现
double dot(Point a, Point b)
{
return a.x * b.x + a.y * b.y;
}
3.3 外积(叉积) AxB = |A||B|sin(C)
(1) 几何意义:向量A与B张成的平行四边形的有向面积。B在A的逆时针方向为正。
(2) 代码实现
double cross(Point a, Point b)
{
return a.x * b.y - b.x * a.y;
}
3.4 常用函数
3.4.1 取模
double get_length(Point a)
{
return sqrt(dot(a, a));
}
3.4.2 计算向量夹角
double get_angle(Point a, Point b)
{
return acos(dot(a, b) / get_length(a) / get_length(b));
}
3.4.3 计算两个向量构成的平行四边形有向面积
double area(Point a, Point b, Point c)
{
return cross(b - a, c - a);
}
3.4.5 向量A顺时针旋转C的角度:
Point rotate(Point a, double angle)
{
return Point(a.x * cos(angle) + a.y * sin(angle), -a.x * sin(angle) + a.y * cos(angle));
}
4. 点与线
4.1 直线定理
(1) 一般式 ax + by + c = 0
(2) 点向式 P0 + v*t (v是方向向量)(计算几何用的最多)(t的取值范围可以限制其为直线、射线、线段)
(3) 斜截式 y = kx + b
4.2 常用操作
(1) 判断点在直线上 A x B = 0
(2) 两直线相交
// cross(v, w) == 0则两直线平行或者重合,这个要先判断
Point get_line_intersection(Point p, Vector v, Point q, vector w)
{
Vector u = p - q;
double t = cross(w, u) / cross(v, w);
return p + v * t;
}
(3) 点到直线的距离
double distance_to_line(Point p, Point a, Point b)
{
ector v1 = b - a, v2 = p - a;
return fabs(cross(v1, v2) / get_length(v1));
}
(4) 点到线段的距离
double distance_to_segment(Point p, Point a, Point b)
{
if (a == b) return get_length(p - a);
Vector v1 = b - a, v2 = p - a, v3 = p - b;
if (sign(dot(v1, v2)) < 0) return get_length(v2);
if (sign(dot(v1, v3)) > 0) return get_length(v3);
return distance_to_line(p, a, b);
}
(5) 点在直线上的投影
double get_line_projection(Point p, Point a, Point b)
{
Vector v = b - a;
return a + v * (dot(v, p - a) / dot(v, v));
}
(6) 点是否在线段上
bool on_segment(Point p, Point a, Point b)
{
return sign(cross(p - a, p - b)) == 0 && sign(dot(p - a, p - b)) <= 0;
}
(7) 判断 a1a2 与 b1b2 两线段是否相交
bool segment_intersection(Point a1, Point a2, Point b1, Point b2)
{
double c1 = cross(a2 - a1, b1 - a1), c2 = cross(a2 - a1, b2 - a1);
double c3 = cross(b2 - b1, a2 - b1), c4 = cross(b2 - b1, a1 - b1);
return sign(c1) * sign(c2) <= 0 && sign(c3) * sign(c4) <= 0;
//即 b1, b2 在 a1 两侧且 a1, a2 在 b1 的两侧。
}
5. 多边形
5.1 三角形
5.1.1 面积
(1) 叉积
(2) 海伦公式
p = (a + b + c) / 2;
S = sqrt(p(p - a) * (p - b) * (p - c));
5.1.2 三角形四心
(1) 外心,外接圆圆心
三边中垂线交点。到三角形三个顶点的距离相等
(2) 内心,内切圆圆心
角平分线交点,到三边距离相等
(3) 垂心
三条垂线交点
(4) 重心
三条中线交点(到三角形三顶点距离的平方和最小的点,三角形内到三边距离之积最大的点)
5.2 普通多边形
通常按逆时针存储所有点
5.2.1 定义
(1) 多边形
由在同一平面且不再同一直线上的多条线段首尾顺次连接且不相交所组成的图形叫多边形
(2) 简单多边形
简单多边形是除相邻边外其它边不相交的多边形
(3) 凸多边形
过多边形的任意一边做一条直线,如果其他各个顶点都在这条直线的同侧,则把这个多边形叫做凸多边形
任意凸多边形外角和均为360°
任意凸多边形内角和为(n−2)*180°
5.2.2 常用函数
(1) 求多边形面积(不一定是凸多边形)
我们可以从第一个顶点除法把凸多边形分成n − 2个三角形,然后把面积加起来。
double polygon_area(Point p[], int n)
{
double s = 0;
for (int i = 1; i + 1 < n; i ++ )
s += cross(p[i] - p[0], p[i + 1] - p[i]);
return s / 2;
}
如果是逆时针储存,求出来的是面积是正的;如果是顺时针存储,求出来的面积是负的。
(2) 判断点是否在多边形内(不一定是凸多边形)
a. 射线法(更常用),从该点任意做一条和所有边都不平行的射线。交点个数为偶数,则在多边形外;为奇数,则在多边形内。
b. 转角法(在多边形内转360度,在多边形外转0度)
(3) 判断点是否在凸多边形内
只需判断点是否在所有边的左边(逆时针存储多边形)(每个边看作向量)。
5.3 皮克定理
皮克定理是指一个计算点阵中顶点在格点上的多边形面积公式该公式可以表示为:
S = a + b/2 - 1
其中a表示多边形内部的点数,b表示多边形边界上的点数,S表示多边形的面积。
必须保证多边形所有顶点的坐标都是整数。
6. 圆(一般都要联立解方程)
(1) 圆与直线交点
(2) 两圆交点
(3) 点到圆的切线
(4) 两圆公切线
(5) 两圆相交面积
( x , y ) ∗ ( c o s θ − s i n θ s i n θ c o n θ ) (x, y) * \begin{pmatrix}cos\theta & -sin\theta\\sin\theta & con\theta\end{pmatrix} (x,y)∗(cosθsinθ−sinθconθ)
#include
#include
#include
#include
using namespace std;
typedef long long ll;
typedef pair<ll, ll> P;
const int maxn = 5010;
int N, M;
P a[maxn], b[maxn];
int ans[maxn];
ll cross(ll x1, ll y1, ll x2, ll y2) {
return x1 * y2 - x2 * y1;
}
ll area(P a, P b, P c) {
return cross(c.first - a.first, c.second - a.second, b.first - a.first, b.second - a.second);
}
int find(int x, int y) {
int lb = 0, ub = N;
while (ub - lb > 0) {
int mid = (lb + ub) / 2;
if (area(b[mid], a[mid], { x, y }) < 0) ub = mid;
else lb = mid + 1;
}
return ub;
}
int main() {
ll x1, y1, x2, y2;
bool is_first = true;
while (cin >> N, N) {
cin >> M >> x1 >> y1 >> x2 >> y2;
memset(ans, 0, sizeof ans);
for (int i = 0; i < N; i++) {
scanf("%lld%lld", &a[i].first, &b[i].first);
a[i].second = y1, b[i].second = y2;
}
a[N] = { x2, y1 }, b[N] = { x2, y2 };
for (int i = 0; i < M; i++) {
ll x, y;
scanf("%lld%lld", &x, &y);
ans[find(x, y)]++;
}
if (!is_first) {
printf("\n");
}
else is_first = false;
for (int i = 0; i <= N; i++) {
printf("%d: %d\n", i, ans[i]);
}
}
return 0;
}
#include
#include
#include
#include
#include
using namespace std;
typedef pair<double, double> P;
const int maxn = 210;
const double EPS = 1e-8;
int N;
P q[maxn], a[maxn], b[maxn];
bool cmp(double x, double y) {
if (fabs(x - y) < EPS) return 0;
else if (x < y) return -1;
else return 1;
}
int sign(double x) {
if (fabs(x) < EPS) return 0;
else if (x < 0) return -1;
else return 1;
}
double cross(double x1, double y1, double x2, double y2) {
return x1 * y2 - x2 * y1;
}
double area(P a, P b, P c) {
return cross(a.first - c.first, a.second - c.second, b.first - c.first, b.second - c.second);
}
bool check() {
for (int i = 0; i < 2 * N; i++) {
for (int j = i + 1; j < 2 * N; j++) {
if (!cmp(q[i].first, q[j].first) && !cmp(q[i].second, q[j].second)) continue;
bool flag = true;
for (int k = 0; k < N; k++) {
if (sign(area(q[i], q[j], a[k])) * sign(area(q[i], q[j], b[k])) > 0) {
//小心这个判断别写错,想清楚是谁在谁的两侧。
flag = false;
break;
}
}
if (flag) return true;
}
}
return false;
}
int main() {
int T;
scanf("%d", &T);
while (T--) {
scanf("%d", &N);
for (int i = 0, k = 0; i < N; i++) {
double x1, y1, x2, y2;
scanf("%lf%lf%lf%lf", &x1, &y1, &x2, &y2);
q[k++] = { x1, y1 }, q[k++] = { x2, y2 };
a[i] = { x1, y1 }, b[i] = { x2, y2 };
}
if (check()) printf("Yes!\n");
else printf("No!\n");
}
}
#include
#include
#include
#include
#include
using namespace std;
typedef pair<double, double> P;
const int maxn = 10010;
P q[maxn];
int stk[maxn], N;
bool used[maxn];
P operator - (P a, P b) {
return { a.first - b.first, a.second - b.second };
}
double cross(P a, P b) {
return a.first * b.second - b.first * a.second;
}
double area(P a, P b, P c) {
return cross(b - a, c - a);
}
double get_dist(P a, P b) {
double dx = a.first - b.first, dy = a.second - b.second;
return sqrt(dx * dx + dy * dy);
}
double Andrew() {
sort(q, q + N);
int top = 0;
for (int i = 0; i < N; i++) {
while (top >= 2 && area(q[stk[top]], q[stk[top - 1]], q[i]) >= 0) {
// 凸包边界上的点即使被从栈中删掉,也不能删掉used上的标记
if (area(q[stk[top - 1]], q[stk[top]], q[i]) < 0)
used[stk[top--]] = false;
else top--;
}
used[i] = true;
stk[++top] = i;
}
used[0] = false;
for (int i = N - 1; i >= 0; i--) {
if (used[i]) continue;
while (top >= 2 && area(q[stk[top]], q[stk[top - 1]], q[i]) >= 0) {
top--;
}
stk[++top] = i;
}
double res = 0;
for (int i = 2; i <= top; i++) {
res += get_dist(q[stk[i - 1]], q[stk[i]]);
}
return res;
}
int main() {
scanf("%d", &N);
for (int i = 0; i < N; i++) scanf("%lf%lf", &q[i].first, &q[i].second);
double ans = Andrew();
printf("%.2f\n", ans);
return 0;
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PIQK8Uk4-1686468593882)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\image-20210124084732974.png)]
#include
#include
#include
#include
#include
using namespace std;
#define x first
#define y second
const int maxn = 40010;
const double PI = acos(-1);
typedef pair<double, double> P;
int N, cnt;
P q[maxn];
int stk[maxn], top;
bool used[maxn];
P rotate(P a, double b) {
return { a.x * cos(b) + a.y * sin(b), -a.x * sin(b) + a.y * cos(b) };
}
P operator - (P a, P b) {
return { a.x - b.x, a.y - b.y };
}
double cross(P a, P b) {
return a.x * b.y - b.x * a.y;
}
double area(P a, P b, P c) {
return cross(b - a, c - a);
}
double get_dist(P a, P b) {
double dx = a.x - b.x;
double dy = a.y - b.y;
return sqrt(dx * dx + dy * dy);
}
double andrew() {
sort(q, q + cnt);
int top = 0;
for (int i = 0; i < cnt; i++) {
while (top >= 2 && area(q[stk[top - 1]], q[stk[top]], q[i]) <= 0) {
if (area(q[stk[top - 1]], q[stk[top]], q[i]) < 0)
used[stk[top--]] = false;
else top--;
}
stk[++top] = i;
used[i] = true;
}
used[0] = false;
for (int i = cnt - 1; i >= 0; i--) {
if (used[i]) continue;
while (top >= 2 && area(q[stk[top - 1]], q[stk[top]], q[i]) <= 0) {
top--;
}
stk[++top] = i;
}
double res = 0;
for (int i = 2; i <= top; i++) {
res += get_dist(q[stk[i - 1]], q[stk[i]]);
}
return res;
}
int main() {
cin >> N;
double a, b, r;
cin >> a >> b >> r;
a = a / 2 - r, b = b / 2 - r;
int dx[] = { 1, 1, -1, -1 }, dy[] = { 1, -1, 1, -1 };
while (N--) {
double x, y, z;
scanf("%lf%lf%lf", &x, &y, &z);
for (int i = 0; i < 4; i++) {
auto t = rotate({ dx[i] * b, dy[i] * a }, -z);
q[cnt++] = { t.x + x, t.y + y };
}
}
double res = andrew();
printf("%.2f\n", res + 2 * PI * r);
}
#include
#include
#include
#include
#include
using namespace std;
const int maxn = 510;
#define x first
#define y second
typedef pair<double, double> P;
int cnt;
struct Line {
P st, ed;
}line[maxn];
int q[maxn]; //双端队列
P pg[maxn], ans[maxn];
//(1)先摆上三个常用函数
P operator -(P a, P b) {
return { a.x - b.x, a.y - b.y };
}
double cross(P a, P b) {
return a.x * b.y - a.y * b.x;
}
double area(P a, P b, P c) {
return cross(b - a, c - a);
}
//(2)求直线的交点
P get_line_intersection(P p, P v, P q, P w) {
auto u = p - q;
double t = cross(w, u) / cross(v, w);
return { p.x + v.x * t, p.y + v.y * t };
}
P get_line_intersection(Line a, Line b) {
return get_line_intersection(a.st, a.ed - a.st, b.st, b.ed - b.st);
}
//(3)写直线的比较函数
const double EPS = 1e-8;
int sign(double x) {
if (fabs(x) < EPS) return 0;
else if (x < 0) return -1;
else return 1;
}
int dcmp(double x, double y) {
if (fabs(x - y) < EPS) return 0;
else if (x < y) return -1;
else return 1;
}
double get_angle(const Line& a) {
//求直线倾斜角 (-PI, PI].
return atan2(a.ed.y - a.st.y, a.ed.x - a.st.x);
}
bool cmp(const Line& a, const Line& b) {
double A = get_angle(a), B = get_angle(b);
if (!dcmp(A, B)) return area(a.st, a.ed, b.ed) < 0;
return A < B;
}
bool on_right(Line& a, Line& b, Line& c) {
//直线 b 和直线 c 的交点在直线 a 的右侧。
auto o = get_line_intersection(b, c);
return sign(area(o, a.st, a.ed)) <= 0;
}
double half_plane_intersection() {
sort(line, line + cnt, cmp);
int hh = 0, tt = -1; //q[hh]表示队头,q[tt]表示队尾, tt - hh + 1 表示队列的大小。
for (int i = 0; i < cnt; i++) {
if (i && dcmp(get_angle(line[i]), get_angle(line[i - 1])) == 0) continue; //角度相同,只保留左边的直线即可。
//队列多于两个元素时,若队尾两直线交点在新的直线右边,则删除队尾元素。
while (tt - hh >= 1 && on_right(line[i], line[q[tt]], line[q[tt - 1]])) tt--;
//同样,对于队头,维护同样的操作。
while (tt - hh >= 1 && on_right(line[i], line[q[hh]], line[q[hh + 1]])) hh++;
q[++tt] = i;
}
//再用队尾更新队头,用队头更新队尾。
while (tt - hh >= 1 && on_right(line[q[hh]], line[q[tt - 1]], line[q[tt]])) tt--;
while (tt - hh >= 1 && on_right(line[q[tt]], line[q[hh]], line[q[hh + 1]])) hh++;
q[++tt] = q[hh];
int k = 0;
//注意,最有一个元素是第一个元素,因此要 i < tt;
for (int i = hh; i < tt; i++) {
ans[k++] = get_line_intersection(line[q[i]], line[q[i + 1]]);
}
double res = 0;
for (int i = 1; i + 1 < k; i++) {
res += area(ans[0], ans[i], ans[i + 1]);
}
//注意,一定是 res / 2,因为求的是三角形的有向面积,cross 函数求的是两向量构成的平行四边形的有向面积。
return res / 2;
}
int main() {
int N, M;
scanf("%d", &N);
while (N--) {
scanf("%d", &M);
for (int i = 0; i < M; i++) {
scanf("%lf%lf", &pg[i].x, &pg[i].y);
}
for (int i = 0; i < M; i++) {
line[cnt++] = { pg[i], pg[(i + 1) % M] };
}
}
double res = half_plane_intersection();
printf("%.3f\n", res);
return 0;
}
#include
#include
#include
#include
using namespace std;
#define x first
#define y second
const int maxn = 100010;
const double eps = 1e-12;
const double PI = acos(-1);
typedef pair<double, double> P;
typedef pair<P, P> Line;
int N;
P q[maxn];
//圆的结构体
struct Circle {
P p;
double r;
};
int sign(double x) {
if (fabs(x) < eps) return 0;
if (x < 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;
}
P operator -(P a, P b) {
return { a.x - b.x, a.y - b.y };
}
P operator +(P a, P b) {
return { a.x + b.x, a.y + b.y };
}
P operator *(P a, double t) {
return { t * a.x, t * a.y };
}
P operator /(P a, double t) {
return { a.x / t, a.y / t };
}
double cross(P a, P b) {
return a.x * b.y - a.y * b.x;
}
double get_dist(P a, P b) {
double dx = a.x - b.x;
double dy = a.y - b.y;
return sqrt(dx * dx + dy * dy);
}
//求两直线交点
P get_line_intersection(P p, P v, P q, P w) {
auto u = p - q;
double t = cross(w, u) / cross(v, w);
return p + v * t;
}
//求中垂线
P rotate(P a, double b) {
return { a.x * cos(b) + a.y * sin(b), -a.x * sin(b) + a.y * cos(b) };
}
Line get_line(P a, P b) {
return { (a + b) / 2, rotate(b - a, PI / 2) };
}
//求外接圆
Circle get_circle(P a, P b, P c) {
auto u = get_line(a, b), v = get_line(a, c);
auto o = get_line_intersection(u.x, u.y, v.x, v.y);
return { o, get_dist(o, a) };
}
int main() {
scanf("%d", &N);
for (int i = 0; i < N; i++) scanf("%lf%lf", &q[i].x, &q[i].y);
random_shuffle(q, q + N);
Circle c = { q[0] , 0 };
for (int i = 1; i < N; i++) {
if (dcmp(c.r, get_dist(c.p, q[i])) < 0) {
c = { q[i], 0 };
for (int j = 0; j < i; j++) {
if (dcmp(c.r, get_dist(c.p, q[j])) < 0) {
c = { (q[i] + q[j]) / 2, get_dist(q[i], q[j]) / 2 };
for (int k = 0; k < j; k++) {
if (dcmp(c.r, get_dist(c.p, q[k])) < 0) {
//三点确定一个圆
c = get_circle(q[i], q[j], q[k]);
}
}
}
}
}
}
printf("%.10f\n", c.r);
printf("%.10f %.10f\n", c.p.x, c.p.y);
return 0;
}
代码和上一道题没啥区别,略。
基础知识:
1. 三维向量表示(x, y, z)
2. 向量加减法、数乘运算,与二维相同
3. 模长 |A| = sqrt(x * x + y * y + z * z)
4. 点积
(1) 几何意义:A·B = |A| * |B| * cos(C)
(2) 代数求解:(x1, y1, z1) · (x2, y2, z2) = (x1x2, y1y2, z1z2);
5. 叉积
(1) 几何意义:AxB = |A| * |B| * sin(C),方向:右手定则
(2) 代数求解:AxB = (y1z2 - z1y2, z1x2 - x1z2, x1y2 - y1x2)
6. 如何求平面法向量
任取平面上两个不共线的向量A、B:AxB
7. 判断点D是否在平面里
任取平面上两个不共线的向量A、B:先求法向量C = AxB,然后求平面上任意一点到D的向量E与C的点积,判断点积是否为0。
8. 求点D到平面的距离
任取平面上两个不共线的向量A、B:先求法向量C = AxB。然后求平面上任意一点到D的向量E在C上的投影长度即可。即:E·C / |C|
9. 多面体欧拉定理
顶点数 - 棱长数 + 表面数 = 2
10. 三维凸包
#include
#include
#include
#include
using namespace std;
const int maxn = 110;
const double eps = 1e-12;
int N, M; //点的总数,三维凸包面的个数
bool g[maxn][maxn]; //记录每个面是否被照到。g[i][j] 表示 i 和 j 相连的边。
double rand_eps() {
return ((double)rand() / RAND_MAX - 0.5) * eps;
}
struct Point {
double x, y, z;
void shake() {
x += rand_eps(), y += rand_eps(), z += rand_eps();
}
Point operator-(Point t) {
return { x - t.x, y - t.y, z - t.z };
}
Point operator+(Point t) {
return { x + t.x, y + t.y, z + t.z };
}
//点乘
double operator&(Point t) { //小心优先级的问题,因为&优先级挺低的。
return x * t.x + y * t.y + z * t.z;
}
//叉乘
Point operator*(Point t) {
return { y * t.z - t.y * z, z * t.x - x * t.z, x * t.y - y * t.x };
}
double len() {
return sqrt(x * x + y * y + z * z);
}
}q[maxn];
struct Plane {
int v[3];
Point norm() {
return (q[v[1]] - q[v[0]]) * (q[v[2]] - q[v[0]]);
}
double area() {
return norm().len() / 2;
}
bool above(Point a) {
return ((a - q[v[0]]) & norm()) >= 0; //一定要加括号,不然优先级会出问题!
}
}plane[maxn], np[maxn]; //开一个备份数组
void get_convex_3d() {
plane[M++] = { 0, 1, 2 };
plane[M++] = { 2, 1, 0 };
for (int i = 3; i < N; i++) {
int cnt = 0;
for (int j = 0; j < M; j++) {
bool t = plane[j].above(q[i]);
if (!t) np[cnt++] = plane[j];
for (int k = 0; k < 3; k++) {
g[plane[j].v[k]][plane[j].v[(k + 1) % 3]] = t;
}
}
for (int j = 0; j < M; j++) {
for (int k = 0; k < 3; k++) {
int a = plane[j].v[k], b = plane[j].v[(k + 1) % 3];
if (g[a][b] && !g[b][a]) { //意味着q[i] 只能照到 plane[j] 的一个面
np[cnt++] = { a, b, i };
}
}
}
M = cnt;
for (int j = 0; j < M; j++) plane[j] = np[j];
}
}
int main() {
scanf("%d", &N);
for (int i = 0; i < N; i++) {
scanf("%lf%lf%lf", &q[i].x, &q[i].y, &q[i].z);
q[i].shake();
}
get_convex_3d();
double res = 0;
for (int i = 0; i < M; i++) {
res += plane[i].area();
}
printf("%.6f\n", res);
return 0;
}