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)Xrauto 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
08
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)Xrauto 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 13
1 1
2 1
1 23
1 1
3 0
2 2*/

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

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Kstry: 业务架构的首选之选

在当今数字化时代&#xff0c;构建稳健且高效的业务架构对于企业的成功至关重要。Kstry作为一种创新性的技术架构&#xff0c;已经在各个领域展现出卓越的表现。本文将探讨为何Kstry被视为业务架构的首选&#xff0c;并介绍其独特的特点和优势。 引言 业务架构是指基于企业战略…

提升代码复用性:探讨Mixin类在面向对象语言中的独特作用和优势

&#x1f3ac; 江城开朗的豌豆&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 &#x1f4dd; 个人网站 :《 江城开朗的豌豆&#x1fadb; 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! 目录 ⭐ 专栏简介 &#x1f4d8; 文章引言 一、m…

es性能强悍的推演过程

前言 es底层复用的Lucene的能力&#xff0c;Lucene在以前的文章中有所讲解&#xff0c;感兴趣可查看 https://blog.csdn.net/u013978512/article/details/125474873?ops_request_misc%257B%2522request%255Fid%2522%253A%2522169771769916777224433628%2522%252C%2522scm%2522…

2个器件,做1个恒流源

在项目中经常要用到恒流源&#xff0c;查找资料可以使用电压源芯片LM317构造一个电流源芯片。本文将电压源加上一个电阻改为电流源&#xff0c;这种设计思路可以扩展到其他类型的电源芯片上&#xff0c;如开关电源及其他类型的线性电源&#xff0c;关键点在于基准电压VREF的使用…

若依Linux与Docker集群部署

若依Linux集群部署 1. 若依2.MYSQL Linux环境安装2.1 MYSQL数据库部署和安装2.2 解压MYSQL安装包2.3 创建MYSQL⽤户和⽤户组2.4 修改MYSQL⽬录的归属⽤户2.5 准备MYSQL的配置⽂件2.6 正式开始安装MYSQL2.7 复制启动脚本到资源⽬录2.8 设置MYSQL系统服务并开启⾃启2.9 启动MYSQL…

MDM如何配置数据审批功能

MDM基础数据平台是进行清洗和治理企业的主数据&#xff0c;使企业的主数据具有唯一性、准确性、一致性、及时性&#xff0c;通过主数据数据清洗功能将错误数据和重复数据进行清洗&#xff0c;从而保证企业数据能够做到以上那几点&#xff0c;然后再分发至下游系统&#xff0c;使…

苹果手机照片如何导入电脑?无损快速的传输办法分享!

前些天小编的朋友联系到我&#xff0c;说是自己苹果手机里面的照片太多&#xff0c;有好几千张&#xff0c;不知道该怎么快而无损地传到电脑。我想遇到这种情况的不止是小编的朋友&#xff0c;生活中遇到手机照片导入电脑的同学不在少数。不管是苹果手机还是安卓手机&#xff0…

μC/OS-II---互斥信号量管理2(os_mutex.c)

目录 背景&#xff1a;优先级反转问题互斥信号量管理互斥信号量发出&#xff08;释放&#xff09;互斥信号量获取/无等待互斥信号量状态查询 背景&#xff1a;优先级反转问题 在高优先级任务等待低优先级任务释放资源时&#xff0c;第三个中等优先级任务抢占了低优先级任务。阻…

【postgresql】查看数据中表的信息

切换到postgresql数据库&#xff0c;各种不适应吧。 有个需求需要查询数据表的各种信息。 下面我们一起学习吧。 ●PostgreSQL: Documentation PostgreSQL: Documentation ●pg_namespace 存储名字空间。名字空间是 SQL 模式下层的结构&#xff1a;每个名字空间有独立的关系…

FPGA时序约束与分析-简单入门

FPGA时序约束与分析-简单入门 文章目录 FPGA时序约束与分析-简单入门1. 本课程概述2. 时序约束简介2.1 什么是时序约束2.2 合理的时序约束2.3 *基于Vivado的时序约束方法 3. 时序分析的基本概念3.1 时钟与时钟偏差3.2 建立时间和保持时间3.3 时序分析中路径、沿和关系的定义 4.…

V10chrony服务配置

Chrony简介 Chrony是一个开源自由的网络时间协议 NTP 的客户端和服务器软软件。它能让计算机保持系统时钟与时钟服务器&#xff08;NTP&#xff09;同步&#xff0c;因此让你的计算机保持精确的时间&#xff0c;Chrony也可以作为服务端软件为其他计算机提供时间同步服务。 Ch…

字符流的讲解 以及 Reader和Writer的用法

文章目录 ❤专栏导读❤字符流❤Reader类的操作 ❤Writer类操作❤Writer类的构造方法 ❤专栏导读 &#x1f680;《多线程》 &#x1f680;《数据结构剖析》 &#x1f680;《JavaSE语法》 在Java标准库中&#xff0c;提供的读写文件的流对象有很多很多的类&#xff0c;但是可以将…

计算机网络之物理层

物理层 1. 物理层的基本概念 2.物理层下面的传输媒体 传输媒体可分为两类&#xff0c;一类是导引型传输媒体&#xff0c;另一类是非导引型传输媒体。 3.传输方式 3.1 串行传输和并行传输 串行传输&#xff1a;串行传输是指数据是一个比特依次发送的&#xff0c;因此在发送端…

家居行业中跨区域EDI应用的优化策略

提到家居行业的国际知名企业&#xff0c;你会想到哪些&#xff1f;宜家IKEA还是家得宝The Home Depot&#xff1f;这些稳居家居行业第一梯队的企业都有哪些共同之处&#xff1f; 无论是北美市场还是欧洲市场&#xff0c;这些企业都有一个共同点&#xff1a;他们采用EDI&#x…

【Redis系列】Redis上设置key,value的时候出现NOAUTH Authentication required提示如何解决?

哈喽&#xff0c;大家好&#xff0c;我是小浪。相信大家在初学一门新的知识点的时候都会遇到各种各样的问题&#xff0c;在网上找了一大堆的解决方案&#xff0c;最后还是无功而返&#xff0c;那么今天博主就记录一下在进行Redis的一些操作中遇到的问题~ 当我们好不容易安装好R…

国际阿里云:提高CDN缓存命中率教程!!!

CDN缓存命中率低会导致源站压力大&#xff0c;静态资源访问效率低。您可以根据导致CDN缓存命中率低的具体原因&#xff0c;选择对应的优化策略来提高CDN的缓存命中率。 背景信息 CDN通过将静态资源缓存在CDN节点上实现资源访问加速。当客户端访问某资源时&#xff0c;如果CDN节…

给在读博士的建议

早上起床第一件事&#xff0c;瞄了眼知乎&#xff01; 发现现在的手机真的很智能&#xff0c;也许是聊天的时候不经意间提了一句&#xff0c;早上就推荐了自己想看到的东西&#xff01; 分享给大家&#xff01; 有哪些给在读博士的建议&#xff1f; 如何通过一句话判断一个博士…

LeetCode(12)时间插入、删除和获取随机元素【数组/字符串】【中等】

目录 1.题目2.答案3.提交结果截图 链接&#xff1a; 380. O(1) 时间插入、删除和获取随机元素 1.题目 实现RandomizedSet 类&#xff1a; RandomizedSet() 初始化 RandomizedSet 对象bool insert(int val) 当元素 val 不存在时&#xff0c;向集合中插入该项&#xff0c;并返回…

Karmada更高效地实现故障转移

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

【QT系列教程】之二创建项目和helloworld案例

文章目录 一、QT创建项目1.1、创建项目1.2、选择创建项目属性1.3、选择路径和项目名称1.4、选择构建项目类型1.5、布局方式1.6、翻译文件&#xff0c;根据自己需求选择1.7、选择套件1.8、项目管理&#xff0c;自行配置1.9、配置完成&#xff0c;系统自动更新配置 二、QT界面介绍…