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

news2024/11/26 10:28:33

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

本期话题:半平面求交

背景知识

学习资料

视频讲解
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

*/

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

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

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

相关文章

excel用RAND函数、或者RAND.NV函数生成随机数、这两个函数的区别

用RAND函数生成大于0小于1的随机数 插入-》函数&#xff1a; 选择RAND函数&#xff1a; 点击“继续”&#xff1a; 点击“确定”&#xff0c;就生成随机数了&#xff1a; 用RAND.NV函数生成一个大于0小于1的随机数 步骤跟RAND函数相同&#xff0c;只不过选择的是RAN…

[RK3568][Android12.0]--- 系统自带预置第三方APK方法

Platform: RK3568 OS: Android 12.0 Kernel: 4.19 Rockchip默认提供了机制来预置第三方APK, 方法很简单&#xff1a; 1. 在device/rockchip/rk3568创建preinstall目录(如果要可卸载&#xff0c;那就创建preinstall_del目录) 2. 将你要预安装的APK放进此目录即可 preinstall 不…

Karmada更高效地实现故障转移

随着云原生技术的发展&#xff0c;其应用场景不断扩大。越来越多的企业开始将应用程序部署在 Kubernetes 集群中&#xff0c;随着 Kubernetes 集群规模的不断扩大&#xff0c;也带来了许多管理挑战&#xff0c;例如多集群间负载均衡、资源调度、故障转移等问题。为了解决这些问…

μC/OS-II---时间管理(os_time.c)

目录 时间管理相关&#xff08;os_time.c&#xff09;Task延迟按时、分、秒、毫秒延时恢复被延时的Task返回系统当前的Tick计数值设置系统的Tick计数值 时间管理相关&#xff08;os_time.c&#xff09; Task延迟 void OSTimeDly (INT32U ticks) {INT8U y; #if OS_CRITI…

MHA实验

MHA: 什么是MHA masterhigh availabulity :基于主库的高可用环境下&#xff1a;主从复制&#xff0c;故障切换 主从的架构&#xff1a; MHA&#xff1a;最少要一主两从 mysql的单点故障问题&#xff0c;一旦主库崩溃&#xff0c;MHA可以在0-30秒内可以自动完成故障切换 M…

1m照片尺寸怎么调?三个方法解决!

为了满足不同的需求&#xff0c;比如上传到网站、存储在移动设备上或传输给他人等&#xff0c;将照片尺寸调整到1M可以有效地减少照片占用的存储空间&#xff0c;同时保持相对较高的图像质量。下面三种好用的方法。 方法一&#xff1a;使用嗨格式压缩大师 1、打开软件&#xf…

VEX —— Half-edges

目录 一&#xff0c;概述 二&#xff0c;等效 三&#xff0c;函数 在一些VEX函数&#xff0c;可将边看成为每个面非共享的半边&#xff1b; 一&#xff0c;概述 在houdini&#xff0c;边通常被视为面之间无方向且共享的&#xff0c;然而&#xff0c;对于一些任务&#xff08…

Stable Diffusion新手村-我们一起完成AI绘画

1.工具搭建 感谢bilibili的"秋葉aaaki"大佬出的整合包&#xff0c;让我们方便下载安装一键启动&#xff0c;去它的网盘里下载 我的显卡设备&#xff0c;暂时还够哈&#xff0c;出图速度还可以1-2分钟比较美的质感画面 下载以后需要解压下sd-webui-aki-v4.4.7z&#…

解决室内种植最大弊端的是方法—植物生长灯

对于“城市农夫”来说&#xff0c;植物在自己的精心照料下开花结果是最好的“心灵鸡汤”。而对于室内种植&#xff0c;其实存在着很大的弊端。 由于室内无法提供足够自然阳光&#xff0c;在一些气候条件不佳或长冬季的地方&#xff0c;自然光照不足会严重限制植物的生长&#…

【华为云IaaS基础三件套之----计算ECS、网络EIP、存储EVS】

MD[华为云IaaS基础三件套----计算、网络、存储] 华为云IaaS基础三件套之----计算ECS、网络EIP、存储EVS 说明: 这里只是简单从计算/网络/存储&#xff0c;进行介绍&#xff0c;阐明云上对于云下的优势&#xff1b;因ECS是三者综合&#xff0c;故最后说明。 1.网络----弹性公…

项目经理为什么要考PMP?PMP考试条件有哪些?

考得PMP&#xff0c;项目经理可以有以下收获&#xff1a; 1、面试条件上&#xff1a;有PMP证书优先&#xff1b; 2、覆盖行业和职位范围广&#xff0c;医疗&#xff0c;互联网&#xff0c;机械&#xff0c;建筑金融&#xff0c;汽车&#xff0c;零售等各行各业&#xff0c;基…

C 语言数组

C 语言数组 在本教程中&#xff0c;您将学习如何使用数组。您将借助示例学习如何声明&#xff0c;初始化和访问数组的元素。 数组是可以存储多个值的变量。例如&#xff0c;如果要存储100个整数&#xff0c;则可以为其创建一个数组。 示例 cint data[100];如何声明数组&…

【Kotlin精简】第8章 协程

1 简介 Kotlin 中的协程提供了一种全新处理并发的方式&#xff0c;您可以在 Android 平台上使用它来简化异步执行的代码。协程是从 Kotlin 1.3 版本开始引入&#xff0c;但这一概念在编程世界诞生的黎明之际就有了&#xff0c;最早使用协程的编程语言可以追溯到 1967 年的 Sim…

软考系统分析师知识点集锦二:系统规划

一、系统规划的步骤 (1)初步调查:根据企业战略目标&#xff0c;分析企业现状以及系统运行状况。(2)确定系统目标:确定系统的服务范围质量等。(3)分析子系统的组成:做系统划分并指定子系统功能。(4)拟定系统的实施方案:分析子系统优先级,确定开发顺序。(5)进行可行性研究:编写可…

【STM32单片机】比赛计时计分系统设计

文章目录 一、功能简介二、软件设计三、实验现象联系作者 一、功能简介 本项目使用STM32F103C8T6单片机控制器&#xff0c;使用OLED显示模块、矩阵按键模块、蜂鸣器等。 主要功能&#xff1a; 系统运行后&#xff0c;OLED默认显示第1节次比赛时间、AB得分。默认是12分钟倒计时…

轻量级数据中台,大中型企业数字化转型首选

随着互联网的高速发展和信息化的普及&#xff0c;企业面对快速增长的数据量和数据种类&#xff0c;要如何高效地管理、整合和分析这些海量数据成为了一大难题。而轻量级数据中台则是面对这种情况而言很好的一种解决方案。 我们可以从以下几个方面来认识数据中台&#xff1a; …

idea自动生成UML图

设置 选择我们UML图需要的部分&#xff01; 选择显示的部分

壹基金爱泽瑞金 安全家园物料配送忙

11月9日到10日&#xff0c;瑞金赋能公益陆续收到壹基金、阿里巴巴公益爱心网友捐赠的社区志愿者救援队队伍物资&#xff0c;马不停蹄地把物资配送到河背街社区、金都社区和沙洲坝镇等项目点&#xff0c;扎实稳妥推进项目有序执行。 在这次物资配送中&#xff0c;志愿者冒雨前行…

上海国际集团党委副书记、总裁刘信义一行莅临ZStack调研指导

11月10日&#xff0c;上海国际集团有限公司党委副书记、总裁刘信义率上海国际集团、上海国资经营及国鑫创投领导莅临上海云轴信息科技有限公司&#xff08;简称“云轴科技ZStack”&#xff09;调研指导&#xff0c;云轴科技ZStack创始人、董事长张鑫&#xff0c;携公司管理团队…

最新宝塔反代openai官方API开发接口详细搭建教程,解决502 Bad Gateway问题

一、前言 宝塔反代openai官方API接口详细教程&#xff0c;实现国内使用ChatGPT502 Bad Gateway问题解决&#xff0c; 此方法最简单快捷&#xff0c;没有复杂步骤&#xff0c;不容易出错&#xff0c;即最简单&#xff0c;零代码、零部署的方法。 二、实现前提 一台海外服务器…