voronoi diagram(泰森多边形) 应用 - Empire Strikes Back

news2024/10/7 18:31:10

欢迎关注更多精彩
关注我,学习常用算法与数据结构,一题多解,降维打击。

voronoi 图求解点击前往

题目链接:https://vjudge.net/problem/URAL-1520

题目大意

有一个城市,形状是圆形。

城市里有很多化工场。

现在想在每个化工厂里放置爆炸伤害半径相同的炸药。使得爆炸可以影响到整个城市。问最小半径是多少。

思考&分析

先求出每个化工厂所在voronoi区域的边。

判断边的交点是否在圆内。

voronoi区域的边与圆的交点,判断是否在voronoi区域的边区域内。

化工厂所在直径与圆的交点也有可能是最远点。

算法细节

1)判断区域与圆有交
可以求出区域所有顶点,判断有没有顶点在圆上或外面。

2)直线与圆求交
在这里插入图片描述
可以利用投影先求到P点,利用直角三角形可知PB’长度
从P点沿AB方向就可以得到B’坐标,同理,反向还有一个交点。

3)特殊情况
只有1个化工厂,取离圆边缘最远的距离。

只有2个化工厂或者所有工厂在一条线上时,不能进行三角化。

此时,对坐标进行排序,每两个最近的点取中垂线为分割线,也要考虑到直径与圆的交点是否为最远点。

算法过程

1个点,2个点,共线特殊处理

一般情况:

step 1 对边界点进行delaunay三角化
step 2 遍历每个边界点,收集邻边,并按照逆时针排序。
求解出每个邻边的中垂线,分别与边界和其他中垂线求交(邻近的中垂线才有交点)判断是否与圆有交集
step 3 求解与圆的交点,并判断交点是否在voronoi区域内。
step 4 直径与圆的交点并判断是否在voronoi区域内

代码

#include<stdio.h>
#include<cmath>
#include <algorithm>
#include <vector>
#include <list>
#include <cstring>
#include <utility>


using namespace std;
const double EPS = 1e-8;

const int N = 1e6 + 10;
const int M = 1e6 + 10;

int cmp(double d) {
	if (abs(d) < EPS)return 0;
	if (d > 0)return 1;
	return -1;
}

class Point {
public:
	double x, y;
	int id;

	Point() {}
	Point(double a, double b) :x(a), y(b) {}
	Point(const Point& p) :x(p.x), y(p.y), id(p.id) {}

	void in() {
		scanf("%lf %lf", &x, &y);
	}
	void out() {
		printf("%f %f\n", x, y);
	}

	double dis() {
		return sqrt(x * x + y * y);
	}

	double dis2() {
		return x * x + y * y;
	}

	Point operator -() const {
		return Point(-x, -y);
	}

	Point operator -(const Point& p) const {
		return Point(x - p.x, y - p.y);
	}

	Point operator +(const Point& p) const {
		return Point(x + p.x, y + p.y);
	}
	Point operator *(double d)const {
		return Point(x * d, y * d);
	}

	Point operator /(double d)const {
		return Point(x / d, y / d);
	}


	void operator -=(Point& p) {
		x -= p.x;
		y -= p.y;
	}

	void operator +=(Point& p) {
		x += p.x;
		y += p.y;
	}
	void operator *=(double d) {
		x *= d;
		y *= d;
	}

	void operator /=(double d) {
		this ->operator*= (1 / d);
	}

	bool operator<(const Point& a) const {
		return x < a.x || (abs(x - a.x) < EPS && y < a.y);
	}

	bool operator==(const Point& a) const {
		return abs(x - a.x) < EPS && abs(y - a.y) < EPS;
	}
};

// 向量操作

double cross(const Point& a, const Point& b) {
	return a.x * b.y - a.y * b.x;
}

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


class Point3D {
public:
	double x, y, z;

	Point3D() {}
	Point3D(double a, double b, double c) :x(a), y(b), z(c) {}
	Point3D(const Point3D& p) :x(p.x), y(p.y), z(p.z) {}

	double dis() {
		return sqrt(x * x + y * y + z * z);
	}

	double dis2() {
		return x * x + y * y + z * z;
	}

	Point3D operator -(const Point3D& p) const {
		return Point3D(x - p.x, y - p.y, z - p.z);
	}

	Point3D operator +(const Point3D& p) const {
		return Point3D(x + p.x, y + p.y, z + p.z);
	}
	Point3D operator *(double d)const {
		return Point3D(x * d, y * d, z * d);
	}

	Point3D operator /(double d)const {
		return Point3D(x / d, y / d, z / d);
	}


	void operator -=(Point3D& p) {
		x -= p.x;
		y -= p.y;
		z -= p.z;
	}

	void operator +=(Point3D& p) {
		x += p.x;
		y += p.y;
		z += p.z;
	}
	void operator *=(double d) {
		x *= d;
		y *= d;
		z *= d;
	}

	void operator /=(double d) {
		this ->operator*= (1 / d);
	}
};

// 向量操作
Point3D cross(const Point3D& a, const Point3D& b) {
	return Point3D(a.y * b.z - a.z * b.y, -a.x * b.z + a.z * b.x,
		a.x * b.y - a.y * b.x);
}

double dot(const Point3D& a, const Point3D& b) {
	return a.x * b.x + a.y * b.y + a.z * b.z;
}


class Line {
public:
	Point front, tail;
	Line() {}
	Line(Point a, Point b) :front(a), tail(b) {}
};

/*
0 不相交
1 相交
0 平行/重合
*/
int cross(const Line& a, const Line& b) {
	Point dir1 = a.front - a.tail;
	Point dir2 = b.front - b.tail;
	if (cmp(cross(dir1, dir2)) == 0) {
		return 0;
	}

	if (cmp(cross(a.front - b.tail, dir2)) * cmp(cross(a.tail - b.tail, dir2)) >= 0)return 0;
	if (cmp(cross(b.front - a.tail, dir1)) * cmp(cross(b.tail - a.tail, dir1)) >= 0)return 0;
	return 1;
}


int inCircle(Point p0, Point p1, Point p2, Point p3) {
	Point d1 = p1 - p0;
	Point d2 = p2 - p0;
	if (cross(d1, d2) < 0)return inCircle(p0, p2, p1, p3); // 保证平面法向向上

	// 构建映射点
	Point3D lift0(p0.x, p0.y, p0.dis2());
	Point3D lift1(p1.x, p1.y, p1.dis2());
	Point3D lift2(p2.x, p2.y, p2.dis2());
	Point3D lift3(p3.x, p3.y, p3.dis2());

	Point3D z1(lift1 - lift0), z2(lift2 - lift0);
	Point3D normal = cross(z1, z2); // 计算平面法向
	double project = dot(normal, lift3 - lift0); // 计算点到平面距离

	return cmp(project);
}



class EdgeDelaunay {
public:
	int id;
	std::list<EdgeDelaunay>::iterator c;
	EdgeDelaunay(int id = 0) { this->id = id; }
};

class Delaunay {
public:
	std::list<EdgeDelaunay> head[N];  // graph
	Point p[N];
	int n = 0;

	void init(int psize, Point ps[]) {
		this->n = psize;
		memcpy(this->p, ps, sizeof(Point) * n);
		std::sort(this->p, this->p + n);
		divide(0, n - 1);
	}

	void addEdge(int u, int v) {
		head[u].push_front(EdgeDelaunay(v));
		head[v].push_front(EdgeDelaunay(u));
		head[u].begin()->c = head[v].begin();
		head[v].begin()->c = head[u].begin();
	}

	void divide(int l, int r) {
		if (r - l <= 1) {  // #point <= 2
			for (int i = l; i <= r; i++)
				for (int j = i + 1; j <= r; j++) addEdge(i, j);
			return;
		}
		int mid = (l + r) / 2;
		divide(l, mid);
		divide(mid + 1, r);

		std::list<EdgeDelaunay>::iterator it;
		int nowl = l, nowr = r;

		for (int update = 1; update;) {
			// 查找左边最低线位置
			update = 0;
			Point ptL = p[nowl], ptR = p[nowr];
			for (it = head[nowl].begin(); it != head[nowl].end(); it++) {
				Point t = p[it->id];
				double v = cross(ptL - ptR, t - ptR);
				if (cmp(v) > 0 || (cmp(v) == 0 && (t - ptR).dis() < (ptL - ptR).dis())) {
					nowl = it->id, update = 1;
					break;
				}
			}
			if (update) continue;
			// 查找右边最低线位置
			for (it = head[nowr].begin(); it != head[nowr].end(); it++) {
				Point t = p[it->id];
				double v = cross(ptR - ptL, t - ptL);
				if (cmp(v) < 0 || (cmp(v) == 0 && (t - ptL).dis() < (ptL - ptR).dis())) {
					nowr = it->id, update = 1;
					break;
				}
			}
		}

		addEdge(nowl, nowr);  // 添加基线

		for (; true;) {
			Point ptL = p[nowl], ptR = p[nowr];
			int ch = -1, side = 0;
			for (it = head[nowl].begin(); it != head[nowl].end(); it++) {
				if (cmp(cross(ptR - ptL, p[it->id] - ptL)) <= 0)continue; // 判断夹角是否小于180
				if (ch == -1 || inCircle(ptL, ptR, p[ch], p[it->id]) < 0) {
					ch = it->id, side = -1;
				}
			}
			for (it = head[nowr].begin(); it != head[nowr].end(); it++) {
				if (cmp(cross(p[it->id] - ptR, ptL - ptR)) <= 0) continue;// 判断夹角是否小于180
				if (ch == -1 || inCircle(ptL, ptR, p[ch], p[it->id]) < 0) {
					ch = it->id, side = 1;
				}
			}
			if (ch == -1) break;  // 所有线已经加完
			if (side == -1) {
				for (it = head[nowl].begin(); it != head[nowl].end();) {
					// 判断是否相交,边缘不算相交
					if (cross(Line(ptL, p[it->id]), Line(ptR, p[ch]))) {
						head[it->id].erase(it->c);
						head[nowl].erase(it++);
					}
					else {
						it++;
					}
				}
				nowl = ch;
				addEdge(nowl, nowr);
			}
			else {
				for (it = head[nowr].begin(); it != head[nowr].end();) {
					// 判断是否相交,边缘不算相交
					if (cross(Line(ptR, p[it->id]), Line(ptL, p[ch]))) {
						head[it->id].erase(it->c);
						head[nowr].erase(it++);
					}
					else {
						it++;
					}
				}
				nowr = ch;
				addEdge(nowl, nowr);
			}
		}
	}

	std::vector<std::pair<int, int> > getEdge() {
		std::vector<std::pair<int, int> > ret;
		ret.reserve(n);
		std::list<EdgeDelaunay>::iterator it;
		for (int i = 0; i < n; i++) {
			for (it = head[i].begin(); it != head[i].end(); it++) {
				ret.push_back(std::make_pair(p[i].id, p[it->id].id));
			}
		}
		return ret;
	}
};

/*
点p 到 p+r 表示线段1
点q 到 q+s 表示线段2
线段1 上1点用 p' = p+t*r (0<=t<=1)
线段2 上1点用 q' = q+u*s (0<=u<=1)
让两式相等求交点 p+t*r = q+u*s
两边都叉乘s
(p+t*r)Xs = (q+u*s)Xs
pXs + t*rXs = qXs
t = (q-p)Xs/(rXs)
同理,
u = (p-q)Xr/(sXr) -> u = (q-p)Xr/(rXs)

以下分4种情况:
1. 共线,sXr==0 && (q-p)Xr==0, 计算 (q-p)在r上的投影在r长度上的占比t0,
计算(q+s-p)在r上的投影在r长度上的占比t1,查看[t0, t1]是否与范围[0,1]有交集。
如果t0>t1, 则比较[t1, t0]是否与范围[0,1]有交集。
t0 = (q-p)*r/(r*r)
t1 = (q+s-p)*r/(r*r) = t0 + s · r / (r · r)
2. 平行sXr==0 && (q-p)Xr!=0
3. 0<=u<=1 && 0<=t<=1 有交点
4. 其他u, t不在0到范围内,没有交点。
*/
pair<double, double> intersection(const Point& q, const Point& s, const Point& p, const Point& r) {
	// 计算 (q-p)Xr
	auto qpr = cross(q - p, r);
	auto qps = cross(q - p, s);

	auto rXs = cross(r, s);
	if (cmp(rXs) == 0)return { -1, -1 }; // 平行或共线
	// 求解t, u
	// t = (q-p)Xs/(rXs)
	auto t = qps / rXs;

	// u = (q-p)Xr/(rXs)
	auto u = qpr / rXs;

	return { u, t };
}


Point oiPs[N];
Delaunay de;
Point lowPoint;
int ind[M];
Point tmpPs[N]; // 存储与边界的交点
int cakeSize[N];

vector<Point> insectCircle(const Point& A, const Point& dir, double r) {
	vector<Point> ans;
	Point P = A + dir * dot(dir, -A);
	double op = abs(cross(dir, A));
	if (cmp(op - r) > 0)return ans;

	double Pb = sqrt(r * r - op * op);
	if (cmp(Pb) == 0) ans.push_back(P);
	else {
		ans.push_back(P + dir * Pb);
		ans.push_back(P - dir * Pb);
	}

	return ans;
}

// 按照极坐标排序
bool sortcmp(int i, int j) {
	Point pi = oiPs[i] - lowPoint;
	Point pj = oiPs[j] - lowPoint;

	// 在上下半区不同侧,上半区优先
	if (cmp(pi.y * pj.y) < 0) return pi.y > pj.y;
	pi /= pi.dis();
	pj /= pj.dis();
	// 有一条为1,0, x大的优化
	if (cmp(pi.x - 1) == 0 || 0 == cmp(pj.x - 1)) return pi.x > pj.x;

	double d = cmp(cross(pi, pj)); // 同侧判断是否逆时针旋转
	return d > 0;
}

bool oneLine(int n) {
	if (n < 3)return true;
	for (int i = 2; i < n; ++i) {
		if (cmp(cross(oiPs[1] - oiPs[0], oiPs[i] - oiPs[0])) != 0) return false;
	}

	return true;
}

void  solve() {
	int n, r;

	scanf("%d%d", &n, &r);
	int a, b;
	for (int i = 0; i < n; ++i) {
		//scanf("%lf%lf", &oiPs[i].x, &oiPs[i].y);
		scanf("%d%d", &a, &b);
		oiPs[i].x = a;
		oiPs[i].y = b;
		oiPs[i].id = i;
	}

	if (n == 1) {
		printf("%.10f\n", r+oiPs[0].dis());
		return;
	}

	double maxRadius = 0;

	// 判断是否共线
	if (oneLine(n)) {
		// 排成一条线每两点垂直平分线为分隔。
		sort(oiPs, oiPs + n);
		for (int i = 0; i < n; ++i) {
			vector<pair<Point, Point>> midLines;
			if (i) {
				Point mid = (oiPs[i] + oiPs[i-1]) / 2;
				Point dir = oiPs[i] - oiPs[i - 1];
				dir = { -dir.y, dir.x }; // 旋转90度
				dir /= dir.dis();
				midLines.push_back({ mid, dir });
			}

			if (i + 1 < n) {
				Point mid = (oiPs[i] + oiPs[i + 1]) / 2;
				Point dir = oiPs[i] - oiPs[i + 1];
				dir = { -dir.y, dir.x }; // 旋转90度
				dir /= dir.dis();
				midLines.push_back({ mid, dir });
			}


			int k = 0;
			// 求直径与圆的交点
			if (oiPs[i].dis() > EPS) {
				tmpPs[k++] = oiPs[i] / oiPs[i].dis() * r;
				tmpPs[k] = -tmpPs[k - 1];
				k++;
			}

			for (auto& midLine : midLines) {
				// 求与圆的交点
				auto cirs=insectCircle(midLine.first, midLine.second, r);

				for (auto& c : cirs) {
					maxRadius = max(maxRadius, (c - oiPs[i]).dis());
				}
				// 排除不在区域内的直径交点
				for (int j = 0; j < k; ++j) {
					// 判断点是否与Lowpoint 在同一半平面内
					if (cmp(cross(oiPs[i] - midLine.first, midLine.second) * cross(midLine.second, tmpPs[j] - midLine.first)) > 0) {
						swap(tmpPs[k - 1], tmpPs[j]);
						k--;
						j--;
						continue;
					}
				}
			}

			// 取直径交点最大值
			for (int j = 0; j < k; ++j) {
				maxRadius = max(maxRadius, (tmpPs[j] - oiPs[i]).dis());
			}
		}

		printf("%.10f\n", maxRadius);
		return;
	}

	oiPs[n] = oiPs[0];

	de.init(n, oiPs);
	auto oiedges = de.getEdge();
	vector<vector<int>> link(n, vector<int>());
	for (auto oie : oiedges) {
		link[oie.first].push_back(oie.second);
	}

	for (int i = 0; i < n; ++i) {
		// 遍历每个边界点,收集邻边,并按照逆时针排序。
		int len = 0;
		for (auto to : link[i]) {
			ind[len++] = to;
		}

		lowPoint = oiPs[i];
		sort(ind, ind + len, sortcmp);
		ind[len] = ind[0];// 添加循环优化

		int k = 0;
		// 求voronoi 边界之间交点
		for (int i = 0; i < len; ++i) {
			Point mid = (lowPoint + oiPs[ind[i]]) / 2;
			Point dir = oiPs[ind[i]] - lowPoint;
			dir = { -dir.y, dir.x }; // 旋转90度

			Point mid2 = (lowPoint + oiPs[ind[i + 1]]) / 2;
			Point dir2 = oiPs[ind[i + 1]] - lowPoint;
			dir2 = { -dir2.y, dir2.x }; // 旋转90度

			// 判断是否为都边界(夹角不能大于180)
			if (cmp(cross(dir, dir2)) > 0) {
				// 求交点 
				auto pr = intersection(mid, dir, mid2, dir2);

				Point ablePoint = mid2 + dir2 * pr.second;
				if (cmp(ablePoint.dis() - r) <= 0) maxRadius = max(maxRadius, (ablePoint - lowPoint).dis());
			}

			// 求直径与圆的交点
			if (lowPoint.dis() > EPS) {
				tmpPs[k++] = lowPoint / lowPoint.dis() * r;
				tmpPs[k] = -tmpPs[k - 1];
				k++;
			}
			dir /= dir.dis();
			// 求与圆的交点
			auto cirs = insectCircle(mid, dir, r);

			for (auto& c : cirs) {
				tmpPs[k++] = c;
			}
		}

		// 排除无效交点
		for (int i = 0; i < len; ++i) {
			Point mid = (lowPoint + oiPs[ind[i]]) / 2;
			Point dir = oiPs[ind[i]] - lowPoint;
			dir = { -dir.y, dir.x }; // 旋转90度
			for (int j = 0; j < k; ++j) {
				// 判断点是否与Lowpoint 在同一半平面内
				if (cmp(cross(lowPoint - mid, dir) * cross(dir, tmpPs[j] - mid)) > 0) {
					swap(tmpPs[k - 1], tmpPs[j]);
					k--;
					j--;
					continue;
				}
			}
		}
		for (int j = 0; j < k; ++j) {
			maxRadius = max(maxRadius, (tmpPs[j] - lowPoint).dis());
		}
	}

	printf("%.10f\n", maxRadius);
	//printf("%d\n", maxSize);
	//printf("%.10f\n", seat.dis());
}



int main() {
	solve();
	return 0;

}

/*
5 10
0 0 
1 0 
0 1 
0 -1
-1 0

5 10
-1 0
0 0
-1 1
-1 -1
-2 0

5 10
0 0
10 0
0 10
0 -10
-10 0


10 3
1 -1 100
2 2 200
-2.5 -2.56 1

10 5
0.2 0 100
1 0 1
2 0 2
3 0 1
4 0 4

10 5
9.89 0 100
1 0 1
2 0 2
3 0 1
4 0 4

10 2
10 0 100
1 0 1


2 5
2 0
-2 0

2 5
2 -2
-2 -2

6 10
1 0
-1 0
2 0
-2 0
3 0
-3 0

6 10
1 -1
-1 -1
2 -1
-2 -1
3 -1
-3 -1

6 10
-4 -1
-1 -1
-5 -1
-2 -1
-6 -1
-3 -1

*/


本人码农,希望通过自己的分享,让大家更容易学懂计算机知识。创作不易,帮忙点击公众号的链接。

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

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

相关文章

matlab双目标定中基线物理长度获取

在MATLAB进行双目摄像机标定时,通常会获得相机的内参,其中包括像素单位的焦距(focal length)以及物理单位的基线长度(baseline)。对于应用中的深度估计和测量,基线长度的物理单位非常重要,因为它直接影响到深度信息的准确性。有时候,您可能只能获取像素单位的焦距和棋…

华为RS设备状态及接口配置命令

1、查看硬件信息 ①查看序列号 查看整机序列号 display esn display sn ②、查看功率 电源功率 display power 查看光模块功率 display transceiver interface gigabitethernet 1/0/0 verbose ③、查看风扇 display fan ④、查看温度 display temperature all ⑤、查看硬…

微信聚合聊天系统的便捷功能:自动发圈,跟圈

快到双十一咯&#xff0c;很多商家和自媒体、运营人都在发圈做运营&#xff0c;所以现在发圈的频率也会比以往的多一些&#xff0c;但事情一多就会担心今天的朋友圈忘记发、漏发或者错过发圈的时间导致错过私域里的好友、客户会错过活动时间。 其实这些都是可以不用担心&#…

Python 正则表达式(RegEx)指南

正则表达式&#xff08;RegEx&#xff09;是一系列字符&#xff0c;形成了一个搜索模式。RegEx 可用于检查字符串是否包含指定的搜索模式。 RegEx 模块 Python 中有一个内置的包叫做 re&#xff0c;它可以用于处理正则表达式。导入 re 模块&#xff1a; import rePython 中的…

怪物猎人世界Mod制作——替换模型、音效

太喜欢《怪物猎人&#xff1a;世界》这款游戏了&#xff0c;在冰原更新后&#xff0c;游戏版本趋于稳定。卡普空做一些bug修复后宣布不再更新此游戏&#xff0c;游戏版本稳定在v15.11.01。从此这个游戏长达三年未更新&#xff0c;我玩了八百小时也未发现什么明显BUG&#xff0c…

Linux多虚拟主机和配置限制访问与日志

目录 一、多虚拟主机 1.配置单网卡多个ip 2.给每个主机站点设置主页 3.测试访问 二、限制访问 1.限制所有 2.放行192.168.0.0/24网段访问 三、日志与状态页 1.定义访客日志 2.状态页配置 一、多虚拟主机 1.配置单网卡多个ip ip address add 192.168.0.231/24 dev e…

阿里云倚天实例已为数千家企业提供算力,性价比提升超30%

在2023云栖大会上&#xff0c;阿里云宣布倚天ECS实例已服务数千家企业&#xff0c;覆盖电商、生命科学、交通物流及游戏等领域&#xff0c;整体算力性价比提升超30%。 2022年&#xff0c;平头哥自研云原生CPU倚天710在阿里云数据中心规模化部署&#xff0c;并向云上企业提供算力…

“AI解析认知+大数据替代推荐”,云汉芯城推出【芯片智选】元器件替代查询工具

面对数以亿计的元器件型号&#xff0c;工程师们往往会遇到经常使用的物料需要替代但难以选型、遍查规格书也无法对应上所有参数&#xff0c;或是新找到的物料资料不全等问题。如何进行高效准确的选型与替代决策是一项极具挑战性的任务。 在此背景下&#xff0c;云汉芯城结合自研…

机器人制作开源方案 | 宠物智能机器人

一、作品简介 作者&#xff1a;陈瑛、卢文博、刘沈军、 浦津、葛望东单位&#xff1a;南京林业大学指导老师&#xff1a;金慧萍、田涛 1. 背景调研及研究意义 1.1背景调研 随着我国社会经济水平的飞速发展和城市化的进程加速推进&#xff0c;居民生活水平有了较 大幅度的提…

如何解决网站被攻击问题:高防服务器与高防CDN的选择

在当今数字时代&#xff0c;网站攻击已经成为严重的威胁&#xff0c;对网站的可用性和数据安全构成潜在风险。为了解决这个问题&#xff0c;企业需要考虑采用高防服务器或高防CDN等防护方案。本文将详细说明这两种方案的优劣势&#xff0c;并分析大、中、小型企业各自适合的防护…

5大自动化测试的Python框架 【实用干货】

自从2018年被评选为编程语言以来&#xff0c;Python在各大排行榜上一直都是名列前茅。 目前&#xff0c;它在Tiobe指数中排名第三个&#xff0c;仅次于Java和C。随着该编程语言的广泛使用&#xff0c;基于Python的自动化测试框架也应运而生&#xff0c;且不断发展与丰富。 因…

VB.NET—Form问题记录

目录 前言: 过程: 总结: 升华: 前言: 分享一个VB.NET遇到的一个问题&#xff0c;开始一直没有解决&#xff0c;这个问题阻碍着我前进成为我路上的拦路虎&#xff0c;千方百计的想要绕过去&#xff0c;但事与愿违怎么也绕不过去&#xff0c;因为运行不了窗体&#xff0c;程序…

【特殊矩阵的压缩存储】

文章目录 特殊矩阵的压缩存储特殊的矩阵 特殊矩阵的压缩存储 矩阵&#xff1a;一个由m x n个元素排成的m行n列的表。 矩阵的常规存储&#xff1a; 将矩阵描述为一个二维数组。 矩阵的常规存储的特点&#xff1a; 可以将元素进行随机存取&#xff1b; 矩阵运算非常简单&#xf…

【Unity实战】最全面的库存系统(二)

文章目录 先来看看最终效果前言箱子库存箱子存储物品玩家背包快捷栏满了,物品自动加入背包修复开着背包拾取物品不会刷新显示的问题将箱子库存和背包分开,可以同时打开完结先来看看最终效果 前言 本期紧跟着上期,继续来完善我们的库存系统,实现箱子库存和人物背包 箱子库…

Docker:本地目录挂载

Docker&#xff1a;本地目录挂载 1. 案例&#xff1a;MySQL容器的数据挂载2. 将MySQL挂载到我们指定的目录 1. 案例&#xff1a;MySQL容器的数据挂载 查看MySQL容器&#xff0c;判断是否有数据卷挂载基于宿主机目录实现MySQL数据目录、配置文件、初始化脚本的挂载 [root172 _…

前端BOM、DOM

文章目录 BOM操作window对象navigator对象&#xff08;了解即可&#xff09;history对象location对象弹出框警告框确认框提示框 计时相关1.过一段时间之后触发&#xff08;一次&#xff09;2.每隔三秒时间触发一次 DOM操作HTML DOM树 查找标签直接查找间接查找 节点操作操作 获…

【ArcGIS模型构建器】06:ArcGIS中DOM批量分幅教程

ArcGIS中利用模型构建器实现DOM批量分幅裁剪。 文章目录 1. 加载数据2. 批量分幅1. 加载数据 批量分幅通常是基于数字正射影像来实现。 数字正射影像(DOM.tif)CASS标准图幅(shp) 2. 批量分幅 单个图幅可以通过裁剪或者按掩膜提取工具来进行,批量分幅采用模型构建器进行。…

单通道Mat元素的访问之data和step属性【C++的OpenCV 第十四课-OpenCV基础强化(三)】

&#x1f389;&#x1f389;&#x1f389; 欢迎来到小白 p i a o 的学习空间&#xff01; \color{red}{欢迎来到小白piao的学习空间&#xff01;} 欢迎来到小白piao的学习空间&#xff01;&#x1f389;&#x1f389;&#x1f389; &#x1f496; C\Python所有的入门技术皆在 我…

ArcGIS for Android 禁止地图旋转

ArcGIS for Android 禁止地图旋转 话不多说&#xff0c;直接上代码&#xff01;&#xff01;&#xff01; public class LoadMap extends AppCompatActivity {// 地图private MapView mapView;private ArcGISMap map;Overrideprotected void onCreate(Bundle savedInstanceSta…

ConnectionError: HTTPSConnectionPool

ConnectionError: HTTPSConnectionPool(host‘zbbfxstatic.figtingdream.com’, port443): Max retries exceeded with url: /api/cache (Caused by NewConnectionError(‘<urllib3.connection.HTTPSConnection object at 0x00000249795AD9A0>: Failed to establish a ne…