AK F.*ing leetcode 流浪计划之半平面求交

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

本期话题:半平面求交

背景知识

学习资料

视频讲解
https://www.bilibili.com/video/BV1jL411C7Ct/?spm_id_from=333.1007.top_right_bar_window_history.content.click&vd_source=fb27f95f25902a2cc94d4d8e49f5f777

文本资料
https://oi-wiki.org//geometry/half-plane/

基本问题转化

在很多题目中,给定的线段是没有方向的。此时,我们需要先把所有的线段都转化成点加向量的方式。使得向量的左边为有效区域。这样就可以使用模板求解了。

要注意的问题

  1. 主要的问题是浮点型的判断大小问题。在排序和判断点与线的关系时都用到浮点型判断。有些题型会卡精度,能用整数判断尽量不要使用浮点判断。
  2. atan2计算比较耗时,可以事先保存。

代码模板

求多边形的核

题目链接:https://vjudge.net/problem/UVA-1571

多边形的核就是取核区域内任意一点,站在该可以观察到多边形内任意一点。

利用半平面求交可以得到多边形的核

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


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

const int N = 2e6 + 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("%.16f %.16f\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 Line {
public:
	Point front, tail;
	double ang;
	int u, v;
	Line() {}
	Line(const Point& a, const Point& b) :front(a), tail(b) {
		ang = atan2(front.y - tail.y, front.x - tail.x);
	}
};

int cmp(const Line& a, const Line& b) {
	//if (a.u == b.u && a.v == b.v)return 0;
	return cmp(a.ang - b.ang);

}


// 点在直线哪一边>0 左边,<0边
double SideJudge(const Line& a, const Point& b) {
	//return cmp(cross(a.front - a.tail, b - a.tail));
	return cross(a.front - a.tail, b - a.tail);
}


int LineSort(const Line& a, const Line& b) {
	int c = cmp(a, b);
	if (c)return c < 0;
	return	cross(b.front - b.tail, a.front - b.tail) > 0;
}

/*
点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, bool &oneline) {
	// 计算 (q-p)Xr
	auto qpr = cross(q - p, r);
	auto qps = cross(q - p, s);

	auto rXs = cross(r, s);
	if (cmp(rXs) == 0) {
		oneline = true;
		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 LineCross(const Line& a, const Line& b, bool &f) {
	Point dira = a.front - a.tail;
	Point dirb = b.front - b.tail;
	bool oneline=false;
	auto p = intersection(a.tail, dira, b.tail, dirb, oneline);
	if (oneline)f = false;
	return a.tail + dira * p.first;
}

class HalfPlane {
public:
	vector<Line> lines;
	vector<int> q;
	vector<Point> t;
	int len;

	HalfPlane() {
		lines.resize(N);
		q.resize(N);
		t.resize(N);
	}

	void reset() {
		len = 0;
	}

	void addLine(const Line& a) {
		lines[len++] = a;
	}

	bool run() {
		sort(lines.begin(), lines.begin() + len, LineSort);

		int l = -1, r = 0;
		q[0] = 0;
		for (int i = 1; i < len; ++i) {
			if (cmp(lines[i], lines[i - 1]) == 0)continue;
			while (r - l > 1 && SideJudge(lines[i], t[r]) < 0)r--;
			while (r - l > 1 && SideJudge(lines[i], t[l + 2]) < 0)l++;
			q[++r] = i;
			bool f=true;
			t[r] = LineCross(lines[q[r]], lines[q[r - 1]], f);
		}
		while (r - l > 1 && SideJudge(lines[q[l + 1]], t[r]) < 0)r--;

		//if (r - l > 1) {
		//	bool f = true;
		//	t[r + 1] = LineCross(lines[q[l + 1]], lines[q[r]], f);
		//	r++;
		//	if (!f)r -= 2;
		//}

		 统计交点
		//l++;
		//vector<Point> ans(r - l);
		//for (int i = 0; i < ans.size(); ++i) {
		//	ans[i] = t[i + l + 1];
		//}


		return r-l>2;
	}
};

Point oiPs[N * 2];
pair<int, int> ori[N * 2];
HalfPlane hp;

int bigDevid(int a, int b) {
	for (int i = max(abs(a), abs(b)); i >= 1; i--) {
		if (a % i == 0 && b % i == 0)return i;
	}
	return 1;
}


void  solve() {
	int n, m = 1;
	//FILE* fp = fopen("ans.txt", "w");
	while (scanf("%d", &n) != EOF && n) {
		int a, b;
		for (int i = 0; i < n; ++i) {
			scanf("%d %d", &a, &b);
			oiPs[i] = Point(a, b);
			ori[i] = { a,b };
		}
		oiPs[n] = oiPs[0];
		ori[n] = ori[0];

		hp.reset();
		for (int i = 0; i < n; ++i) {
			hp.addLine(Line(oiPs[i+1], oiPs[i]));
			hp.lines[i].u = ori[i+1].first - ori[i].first;
			hp.lines[i].v = ori[i+1].second - ori[i].second;

			int bd = bigDevid(hp.lines[i].u, hp.lines[i].v);
			hp.lines[i].u /= bd;
			hp.lines[i].v /= bd;
		}

		auto ps = hp.run();
		if (ps)puts("1");
		else puts("0");
		m++;
	}
}


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

}

/*
4
0 0
0 1
1 1
1 0
8
0 0
3 0
4 3
2 2
3 4
4 4
4 5
0 5
0


8
0 0
0 1
1 1
1 2
0 2
0 3
3 3
3 0
*/

练习一

链接:https://www.luogu.com.cn/problem/P4196

求多个凸多边形的交面积。

对每条边进行半平面求交,再利用三角形求多边形面积。


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


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

const int N = 2e6 + 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("%.16f %.16f\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 Line {
public:
	Point front, tail;
	double ang;
	int u, v;
	Line() {}
	Line(const Point& a, const Point& b) :front(a), tail(b) {
		ang = atan2(front.y - tail.y, front.x - tail.x);
	}
};

int cmp(const Line& a, const Line& b) {
	//if (a.u == b.u && a.v == b.v)return 0;
	return cmp(a.ang - b.ang);

}


// 点在直线哪一边>0 左边,<0边
double SideJudge(const Line& a, const Point& b) {
	//return cmp(cross(a.front - a.tail, b - a.tail));
	return cross(a.front - a.tail, b - a.tail);
}


int LineSort(const Line& a, const Line& b) {
	int c = cmp(a, b);
	if (c)return c < 0;
	return	cross(b.front - b.tail, a.front - b.tail) > 0;
}

/*
点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, bool &oneline) {
	// 计算 (q-p)Xr
	auto qpr = cross(q - p, r);
	auto qps = cross(q - p, s);

	auto rXs = cross(r, s);
	if (cmp(rXs) == 0) {
		oneline = true;
		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 LineCross(const Line& a, const Line& b, bool &f) {
	Point dira = a.front - a.tail;
	Point dirb = b.front - b.tail;
	bool oneline=false;
	auto p = intersection(a.tail, dira, b.tail, dirb, oneline);
	if (oneline)f = false;
	return a.tail + dira * p.first;
}


class HalfPlane {
public:
	vector<Line> lines;

	void addLine(const Line& a) {
		lines.push_back(a);
	}

	vector<Point> run() {
		sort(lines.begin(), lines.end(), LineSort);
		vector<int> q(lines.size() + 10);
		vector<Point> t(lines.size() + 10);

		int l = -1, r = 0;
		q[0] = 0;
		for (int i = 1; i < lines.size(); ++i) {
			if (cmp(lines[i], lines[i - 1]) == 0)continue;
			while (r - l > 1 && SideJudge(lines[i], t[r]) < 0)r--;
			while (r - l > 1 && SideJudge(lines[i], t[l + 2]) < 0)l++;
			q[++r] = i;
			bool f = true;
			t[r] = LineCross(lines[q[r]], lines[q[r - 1]], f);
		}
		while (r - l > 1 && SideJudge(lines[q[l + 1]], t[r]) < 0)r--;
		if (r - l > 1) {
			bool f = true;
			t[r + 1] = LineCross(lines[q[l + 1]], lines[q[r]], f);
			r++;
		}

		// 统计交点
		l++;
		vector<Point> ans(r - l);
		for (int i = 0; i < ans.size(); ++i) {
			ans[i] = t[i + l + 1];
		}

		return ans;
	}
};

Point oiPs[N];

void  solve() {
	int n, m;
	scanf("%d", &n);
	HalfPlane hp;
	int a, b;
	while (n--) {
		scanf("%d", &m);
		for (int i = 0; i < m; ++i) {
			scanf("%d%d", &a, &b);
			oiPs[i].x = a;
			oiPs[i].y = b;
		}

		oiPs[m] = oiPs[0];
		for (int i = 0; i < m; ++i) {
			hp.addLine(Line(oiPs[i + 1], oiPs[i]));
		}
	}

	auto keyPoints = hp.run();

	double ans = 0;
	for (int i = 2; i < keyPoints.size(); ++i) {
		ans += cross(keyPoints[i - 1] - keyPoints[0], keyPoints[i] - keyPoints[0]);
	}

	printf("%.3f\n", ans / 2);
}


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

}

/*
3
3
-1 2
-2 1
-1 1

3
1 1
2 1
1 2

3
1 1
3 0
2 2

*/

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何解决python2和Python3共存的问题

需要对python2和python3都设置环境变量 需要修改Python编译器的名字&#xff1a;需要将默认的Python编辑器的名称进行修改。 修改Python2安装目录下&#xff1a;python.exe修改为python2.exe&#xff0c;pythonw.exe修改为pythonw2.exe 修改Python3安装目录下&#xff1a;pyt…

10-18 请求与相应1

前后台联调 前台通过一个表单, action写的servlet绑定的url,提交表单,请求我们servlet的doGet()/ doPost()方法 问题: 1.后台怎么获取前端的提交,请求的数据?底层:TCP通信,socket的得到输入流,读取数据 2.后台处理请求之后,怎么把结果给到前端?底层:TCP通信,socket的得到输入…

冲击900亿美元估值!邀约路演、秘密交表的Shein上市有望

双十一的狂欢刚刚结束&#xff0c;Shein即将赴美上市的消息又在电商圈里投下一枚重磅炸弹。 继被媒体曝光其寻求900亿美金估值后&#xff0c;最新的消息称其已邀请投资人参与路演&#xff0c;且已秘密完成交表。这个神秘的中国独角兽&#xff0c;离敲钟登陆美股的日子越来越近…

许战海方法论新成果《全球产业技术品牌竞争战略》研究报告正式发布

在全球化趋势下&#xff0c;产业技术正在加速迭代、产业结构也在持续优化。企业要想从愈发激烈的市场竞争中脱颖而出&#xff0c;需要更强大的竞争力。 在全球产业研究项目中&#xff0c;许战海咨询深入剖析了潍柴动力、利乐、奥迪、霍尼韦尔等一众在产业竞争中脱颖而出的企业…

HDP集群Kafka开启SASLPLAINTEXT安全认证

hdp页面修改kafka配置 java代码连接kafka增加对应的认证信息 props.put("security.protocol","SASL_PLAINTEXT");props.put("sasl.mechanism","PLAIN");props.put("sasl.jaas.config","org.apache.kafka.common.securi…

SQL之回炉重造

重新学sql&#xff0c;整个知识框架出来&#xff0c;之前学的太烂了 SQL是什么&#xff1a; SQL 是一种操作数据库的语言&#xff0c;包括创建数据库、删除数据库、查询记录、修改记录、添加字段等。SQL 虽然是一种被 ANSI 标准化的语言&#xff0c;但是它有很多不同的实现版…

【Python】爬虫代理IP的使用+建立代理IP池

目录 前言 一、代理IP 1. 代理IP的获取 2. 代理IP的验证 3. 代理IP的使用 二、建立代理IP池 1. 代理IP池的建立 2. 动态维护代理IP池 三、完整代码 总结 前言 在进行网络爬虫开发时&#xff0c;我们很容易遭遇反爬虫机制的阻碍。为了规避反爬虫机制&#xff0c;我们…

DNA甲基化的相关知识

目录 1. DNA甲基化简介 2. 原理 3. 酶分类 4. DNA甲基化类型 5.机制 6. 十大DNA甲基化研究核心问题 6.1 植物中的甲基化 6.2 植物中DNA甲基化的主要功能 6.3 DNA甲基化作为生物标志物的潜力 6.4 DNA甲基化检测方法 1. DNA甲基化简介 DNA甲基化&#xff08;DNA methy…

【NI-DAQmx入门】触发相关

触发概述 触发采集为用户提供了两个主要好处&#xff1a;它对输入信号相对于触发事件进行计时&#xff0c;因此用户仅捕获感兴趣区域中的信号&#xff0c;从而节省硬件带宽和内存。 模拟触发和数字触发 模拟触发和数字触发的区别在于触发源的不同。数字触发是一种 TTL 信号&am…

C语言之深入指针(四)

C语言之深入指针 1 函数指针变量 1.1 函数指针变量的创建 整型指针是存放整型数据的指针 数组指针是存放数组地址的指针 那么函数指针变量就是存放函数地址的指针&#xff0c;可以通过函数的地址来调用函数 那么函数是否有自己的地址呢?我们可以写一段代码来看一下 #incl…

世界互联网大会|云轴科技ZStack受邀分享云原生超融合

11月8日至10日“世界互联网大会乌镇峰会”在浙江嘉兴的乌镇开幕&#xff0c;大会的主题为“建设包容、普惠、有韧性的数字世界——携手构建网络空间命运共同体”&#xff0c;全球各界代表就热点焦点问题展开讨论&#xff0c;反映产业各界对互联网发展的前瞻思考&#xff0c;引领…

UE4 / UE5 内存与性能优化

性能优化 资源压缩粒子优化NavMeshGenerate Overlap EventGCMarkTime光照优化Shader优化卡的时间长LOD官方CPU、GPU Insights、stat cpu、 ue4 memory report、inter GPA 、RenderDOC、减少模型面数等 资源压缩 在图片资源上右键选择Size Map可以看到资源所占大小&#xff0c;…

可以通过电脑远程控制安卓设备的软件

有些时候&#xff0c;我们需要用电脑远程控制安卓设备&#xff0c;比如远程维护门店设备、安卓系统的户外广告牌等等。我们来探索和比较几款允许用户通过电脑远程控制安卓设备的软件。 1、Splashtop Business Splashtop 是一种多功能远程访问解决方案&#xff0c;以其高性能流…

【大语言模型】Docker部署清华大学ChatGLM3教程

官方地址&#xff1a;https://github.com/THUDM/ChatGLM3 1 将代码保存至本地 方法1&#xff1a; git clone https://github.com/THUDM/ChatGLM3 方法2&#xff1a; https://github.com/THUDM/ChatGLM3/archive/refs/heads/main.zip 2 创建Docker文件 注&#xff1a;请先…

verdi merge fsdb出现信号冲突的解决办法

前段时间介绍了verdi用 Edit Virtual File的方式把几个fsdb文件merge起来的方法 由于当时实验的时候只用了两个小的fsdb文件&#xff0c;每个fsdb文件中包含的信号量也比较少&#xff0c;所以并没有发现问题 我是用 Edit Virtual FIle把dump不同hier的fsdb文件merge到一起&am…

2023年03月 Python(五级)真题解析#中国电子学会#全国青少年软件编程等级考试

Python等级考试(1~6级)全部真题・点这里 一、单选题(共25题,每题2分,共50分) 第1题 已知一个列表lst = [2,3,4,5,6],lst.append(20),print(lst)的结果是?( )(2分) A.[10,2,3,4,5,6,20] B.[20,2,10,3,4,5,6] C.[2,3,4,5,6,20] D.[2,3,4,5,6,10,20] 答案:C 第2…

uni-app报错“本应用使用HBuilderX x.x.x 或对应的cli版本编译,而手机端SDK版本是x.x.x不匹配的版本可能造成应用异常”

uniapp开发的一个跨平台软件&#xff0c;在安卓模拟器上启动的时候报警告&#xff1a; 官方给的解释&#xff1a;uni-app运行环境版本和编译器版本不一致的问题 - DCloud问答 解决办法有两个 方法一&#xff1a;添加忽略警告的配置 项目根目录下找到 manifest.json&#xf…

LeetCode 1334. 阈值距离内邻居最少的城市:多次运用单源最短路的迪杰斯特拉算法

【LetMeFly】1334.阈值距离内邻居最少的城市&#xff1a;多次运用单源最短路的迪杰斯特拉算法 力扣题目链接&#xff1a;https://leetcode.cn/problems/find-the-city-with-the-smallest-number-of-neighbors-at-a-threshold-distance/ 有 n 个城市&#xff0c;按从 0 到 n-1…

【kafka】 查看节点的消息

对于初学者来说&#xff0c;可能想去节点看看有没有消息产生和消费&#xff0c;可以去kafka的bin目录下执行这个命令&#xff1a; kafka-console-consumer.bat --bootstrap-server localhost:9092 --topic myTopic --from-beginning 这个命令可以理解为&#xff1a;生产过的消…

Python 爬虫之scrapy 库

文章目录 总的介绍相关模块 总的介绍 Scrapy是一个用于爬取网站数据的开源Python框架。它提供了一套强大而灵活的工具&#xff0c;用于从网站上提取所需的数据。Scrapy是基于Twisted异步网络库构建的&#xff0c;因此可以高效地处理大量的并发请求。以下是Scrapy的一些主要特点…