算法模板(7):计算几何(1)

news2024/9/28 3:23:55

计算几何

基础知识

y总总结知识点

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) 两圆相交面积
  • 有向面积:两个向量做叉积,得到的向量指向纸外是正值,指向纸内是负值。
  • A ( x , y ) A(x, y) A(x,y) O O O 顺时针旋转 θ \theta θ

( 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θ)

2983. 玩具

  • 每当约翰将玩具扔进收纳盒中时,确定每个分区中有多少个玩具。

1.jpg

  • 判断点在向量的左侧还是右侧,可以用向量叉积的正负来表示。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
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;
}

2984. 线段

  • 在二维平面内有 n 条线段,请你编写一个程序,判断是否存在一条直线满足将这 n 条线段投影到该直线上后,所有的投影线段至少具有一个公共点。
  • 只要能找到一条穿过所有线段的直线,那么做一条与这条直线垂直的直线,那这条垂直的直线一定满足要求。
  • 用旋转直线的方式,那么它必然旋转着旋转着会卡在某一个端点上。换言之,我们只需要找到是否存在过所有线段至少一个端点的直线,就等价于能否找到上述直线。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
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");

	}
}

凸包

  • 凸包指的是周长最小,而不是面积最小。

1401. 围住奶牛

  • Andrew 算法
  1. 将所有点排序,x 为第一关键字,y 为第二关键字。
  2. 从左至右维护上半部分,从右至左维护下半部分。如果这条边在栈顶元素构成向量的左侧,就把栈顶元素删掉,在右侧就留下来,如果在栈顶元素向量上,看题目要求。
  • 题意:现在给定这些地点的具体坐标,请你求出将这些地点都包含在内的围栏的最短长度是多少(围栏边上的点也算处于围栏内部)。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
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;
}

2935. 信用卡凸包

  • 题意:信用卡是一个矩形,唯四个角作了圆滑处理,使它们都是与矩形的两边相切的 1 4 \frac{1}{4} 41 圆。现在平面上有一些规格相同的信用卡,试求其凸包的周长。注意凸包未必是多边形,因为它可能包含若干段圆弧。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PIQK8Uk4-1686468593882)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\image-20210124084732974.png)]

  • 圆心角的大小等于两切线的夹角。这个性质要用好。
  • 观察可发现,所有圆心构成凸包,加上圆弧长度就是答案。而圆弧的长度是有圆心角之和决定,我们发现圆心角之和等于凸包的外角之和。任意凸多边形外角和是360度。因此凸包长度加上 2 π r 2\pi r 2πr 即可。
  • 注意这个题绕中心旋转的处理,是现在原点旋转,再平移。
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
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);
}

半平面交

  • 给一些有向直线,每次只保留直线左侧部分的平面。凸多边形砍一刀仍是凸多边形。
  • 如果题目强行要求保留右半部分,只需要把直线方向反过来就行。
  • 步骤
  1. 先将所有向量按角度排序 atan2(y, x);如果角度相同,则靠左边的直线优先。
  2. 按顺序扫描所有向量,用双端队列维护。主要就是判端队列中的交点是否在直线右侧。
image-20210124113347948

2803. 凸多边形

  • 题意:逆时针给出 n 个凸多边形的顶点坐标,求它们交的面积。
  • 输入所有点,点两两按顺序形成直线,全部保存在一个数组中,然后对这些点做半平面交。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
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;
}

最小圆覆盖

  • 最小覆盖圆是唯一的,这个可以用反证法去证。
  • 若 P 不在 S 的最小覆盖圆的内部,则 P 一定在 { P } ∪ S \{P\} \cup S {P}S 的最小覆盖圆的边上。
  • 求三个点的外接圆

3028. 最小圆覆盖

  • 在一个二维平面上给定 N 个点,请你画出一个最小的能够包含所有点的圆。圆的边上的点视作在圆的内部。
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
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;
}

2785. 信号增幅仪

  • 题意:无线网络基站在理想状况下有效信号覆盖范围是个圆形,而无线基站的功耗与圆的半径的平方成正比。增幅仪能够在不增加无线基站功耗的前提下,使得有效信号的覆盖范围在某一特定方向上伸长若干倍。
  • 先把所有点旋转(注意与增幅仪旋转方向相反)就相当于椭圆与坐标轴平行了。此题椭圆的方程为 x 2 p 2 + y 2 = r 2 \frac{x^2}{p^2} + y^2 = r^2 p2x2+y2=r2. 因此,我们再把所有点的横坐标都变为 x ′ = x p x' = \frac{x}{p} x=px,就变成了典型的最小圆覆盖问题。

代码和上一道题没啥区别,略。

三维计算几何基础与三维凸包

基础知识:

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的点积,判断点积是否为08. 求点D到平面的距离
    任取平面上两个不共线的向量A、B:先求法向量C = AxB。然后求平面上任意一点到D的向量E在C上的投影长度即可。即:E·C / |C|
9. 多面体欧拉定理
    顶点数 - 棱长数 + 表面数 = 2
10. 三维凸包

2119. 最佳包裹

  • 题意:输入包括该产品的顶点的个数,以及所有顶点的坐标;请计算出包裹这个产品所需要的材料的最小面积
  • 其实就是求三维凸包,通常是一个 O ( n 2 ) O(n^2) O(n2) 的暴力求法。
  • 用逆时针方向三个点维护一个面。加入了随机扰动,防止四个点共面的情况,因此就不用加入近似的判断了,比如 sign 与 dcmp 函数。这样可以有效防止四点共面。
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/637296.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【通用方法】返回近 dayNum 天日期数组封装方法

代码如下 // 返回近 dayNum 天日期数组 getRecentDay(dayNum) {let currentDate new Date();let dayNumDaysAgo new Date(currentDate.getTime() - dayNum * 24 * 60 * 60 * 1000);let recentDays [];for (let i 0; i < dayNum; i) {let date new Date(dayNumDaysAgo.g…

3U VPX XC7VX690T计算处理板

3U VPX 计算处理 板卡外观&#xff1a; 板载FPGA介绍&#xff1a;XC7VX690TT-2FFG1761i 同Xilinx公司的Kintex-7 FPGA系列比起来&#xff0c;Virtex-7系列有更高的性能。国内应用相当广泛。 Virtex-7 Family: Optimized for highest system performance and capacity with a 2X…

OpenCV如何确认是否使用了libjpg-turbo

项目中使用了图片解码的功能&#xff0c;目前使用的是OpenCV里头的libjpg&#xff0c;但是我们可以把 libjpg-turbo li编译到OpenCV中来提高解码效率&#xff0c;据官网说可以提高2-6的效率&#xff1a; 1、使用cv::getBuildInformation()可以看到构建参数。 2、本地构建完成之…

华为交换机观察口(observe-port)配置

镜像是指将经过指定端口&#xff08;源端口或者镜像端口&#xff09;的报文复制一份到另一个指定端口&#xff08;目的端口或者观察端口&#xff09;。 镜像可以在不影响设备对报文进行正常处理的情况下&#xff0c;将镜像端口的报文复制一份到观察端口。 端口镜像是指设备复制…

vscode连接远程服务器出现XHR faild

参考&#xff1a;https://zhuanlan.zhihu.com/p/426876766 1、出现XHR Faild&#xff0c;仔细查看是dowloading vscode-server to host错误&#xff0c;说明在下载vscode-server包出错&#xff0c;可以利用以下方法&#xff0c;手动下载: https://vscode.cdn.azure.cn/stable…

Debezium UI On ECS编译安装及开放Web访问

1. 访问debezium-ui的代码仓库&#xff0c;下载源码 GitHub - debezium/debezium-ui: A web UI for Debezium; Please log issues at https://issues.redhat.com/browse/DBZ. 2. 解压zip源码包&#xff1a; TEST[hadoopshdcvfsla1894 ~]$ cd /data/module TEST[hadoopshd…

Linux防火墙学习笔记6

制定iptables规则策略&#xff1a; 黑名单&#xff1a; 没有被拒绝的流量都可以通过&#xff0c;这种策略下管理员必须针对每一种新出现的攻击&#xff0c;制定新的规则&#xff0c;因此不推荐。 白名单&#xff1a; 没有被允许的流量都要被拒绝&#xff0c;这种策略比较保…

35岁找工作,这个最重要

最近一些35岁左右的中年失业朋友找我聊&#xff0c;我发现他们找工作的方式和年轻人并没有什么不同&#xff0c;还是通过网络APP进行海投。 如果你刚开始工作的时候&#xff0c;通过这种方式去找工作&#xff0c;无可厚非&#xff0c;但如果工作很久了&#xff0c;还通过这种方…

r2pm -ci r2ghidra 时报错:checking pkg-config flags for r_core... no

参考网址&#xff1a; sys/python.sh fails with checking pkg-config flags for r_core... no Issue #1943 radareorg/radare2 GitHub 进入目录/root/.local/share/radare2/r2pm/git/r2ghidra查看configure文件&#xff0c;查找报错位置 执行指令 &#xff1a; # pkg-co…

C++编译链接模型

编译&#xff1a;将源代码翻译成目标代码

如何利用IP风险画像来保护您的账户安全?

在数字时代&#xff0c;网络欺诈已成为金融行业的主要挑战之一。黑客和犯罪分子利用各种技术手段&#xff0c;试图窃取您的个人账户信息&#xff0c;并非法获取您的财产。为了保护客户的账户和资产&#xff0c;可以利用IP风险画像来提供更安全的服务。 IP风险画像是通过分析网络…

linux基本功只10个高效学习Linux命令行工具的技巧:Pandoc实战

前言 大家好&#xff0c;又见面了&#xff0c;我是沐风晓月&#xff0c;本文是专栏【linux基本功-基础命令实战】的第65篇文章。 专栏地址&#xff1a;[linux基本功-基础命令专栏] &#xff0c; 此专栏是沐风晓月对Linux常用命令的汇总&#xff0c;希望能够加深自己的印象&am…

从1万到1亿需要多少个涨停板?(python)

如果本金只有1万元&#xff0c;需要多少个涨停板才可以到达一亿元呢&#xff1f; 亦或者&#xff0c;如果有一亿元本金&#xff0c;需要多少个跌停板才可以到达一万元。 注&#xff1a;涨停板&#xff08;10%&#xff09;&#xff0c;跌停板&#xff08;-10%&#xff09; 用到的…

Android 高仿今日头条新闻客户端,可作为毕业设计

源码下载地址&#xff1a;https://download.csdn.net/download/yujun2023/87897511 背景 一直都想尝试开发自己还没接触过的某类APP&#xff0c;以前刚入门的时候&#xff0c;就有一个梦想&#xff1a;开发社交类、地图类、新闻类、支付、电商类、直播类、游戏类这些APP。社交…

出海如何从0到1?融云《社交泛娱乐出海作战地图》实战经验揭秘

经过近几年的发展&#xff0c;如今的互联网出海已经是截然不同的命题。关注【融云全球互联网通信云】了解更多 从粗放到精细&#xff0c;风浪越来越猛烈。如何契合自己的基因选择赛道和地区、如何打造有获客抓手的独特产品、如何拿下第一个客户&#xff0c;是每个出海人都需要…

FastDFS高可用集群部署安装

1、环境信息&#xff1a; 服务器部署服务16.32.15.200Tracker(调度工作)、Storage(存储)、Nginx、Keepalived16.32.15.201Tracker(调度工作)、Storage(存储)、Nginx、Keepalived16.32.15.202以上两台的VIP地址 2、部署FastDFS 正常部署 FastDFS 此处省略,参考&#xff1a;Fa…

【C++11】移动赋值 | 新的类功能 | 可变参数模板

文章目录 1. 移动赋值2. 新的类的功能移动构造移动赋值defaultdelete 3.可变参数模板可变参数包的解析 文章目录 1. 移动赋值2. 新的类的功能移动构造移动赋值defaultdelete 3.可变参数模板可变参数包的解析 1. 移动赋值 C11中&#xff0c;string中的operator 包含 参数为右值的…

phpstorm+xdebug/php项目调试

前提&#xff1a;项目使用xampp集成 一、下载xdebug&#xff0c;当到xampp/php/exp目录下 二、配置php.ini [Xdebug] zend_extension"D:/xampp/php/ext/php_xdebug.dll" xdebug.collect_paramsOn xdebug.collect_returnOn xdebug.auto_traceOn xdebug.trace_output_…

asp.net探头监控管理系统VS开发sqlserver数据库web结构c#编程Microsoft Visual Studio

一、源码特点 asp.net探头监控管理系统 是一套完善的web设计管理系统&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为vs2010&#xff0c;数据库为sqlserver2008&#xff0c;使用c#语言 开发 asp.net探头监控管理系统VS开发s…

BUUCTF 大帝的密码武器 1

题目描述&#xff1a;&#xff08;下载题目&#xff0c;然后修改后缀名为.zip打开&#xff1a;&#xff09; 公元前一百年&#xff0c;在罗马出生了一位对世界影响巨大的人物&#xff0c;他生前是罗马三巨头之一。他率先使用了一种简单的加密函&#xff0c;因此这种加密方法以…