模板:半平面交(计算几何)

所谓半平面交,就是和“半平先生”当面交谈。顾名思义,这是一个源于日本的算法。

(逃)

前言

感觉应用很灵活的一个算法,一切有两个变量的线性规划问题都可以转化为半平面交。
有时可能要注意取等问题(指射箭),但多数时候似乎并不用

解析

半平面的表示

首先,我们需要一个简洁的方式表达半平面。
不禁想到,我们可以利用叉积的符号来判断点是在一条线的顺时针还是逆时针方向。那么类似的,我们可以用一个点x和一个方向向量d表示一个半平面,一个点p属于这个半平面,当且仅当 d→×XP→>0\overrightarrow{d}\times \overrightarrow{XP}>0d×XP>0(也就是点在方向向量的左侧),是否取等取决于半平面本身的开闭。

inline bool in(const line &l,const V o){return (l.d^(o-l.a))>eps;}
inline bool out(const line &l,const V o){return (l.d^(o-l.a))<-eps;}

流程

有了一个合适的表示方法后,我们就可以开始尝试解决半平面交问题了,顾名思义,这个问题就是:

给出若干个半平面,求它们的交。

(显然,得到的一定是一个凸包。)
怎么做呢?
首先,我们把所有点半平面按照极角排序,极角可以利用 atan2(y,x) 函数很方便的求出。
当两个向量共线时,显然只有靠左的那个是有用的,我们让有用的放在前面。(这样在后面的算法中遇到没用的时候可以直接跳过,避免求平行线的交点而出现错误)

bool cmp(const line l1,const line l2){double t1=atan2(l1.d.y,l1.d.x),t2=atan2(l2.d.y,l2.d.x);if(abs(t1-t2)>eps) return t1<t2;return in(l2,l1.a);
}

考虑维护一个双端队列来维护当前对答案有用的半平面,并记录每个半平面和前一个半平面的交点。
维护分为如下步骤:

  1. 每加入一个半平面时, 若队尾与队尾前一个的交点在当前半平面外,则不断弹出队尾。
  2. 类似的,若队首和后一个的交点在当前半平面外,也不断弹出队首。
  3. 加入当前半平面,并计算其与上一个半平面的交点。
  4. 插入所有半平面后,若队尾与上一个的交点在队首半平面外,那么队尾也是无用的,弹出队尾所有没有用的半平面。
  5. 最后,插入队尾与队首的交点,得到的封闭的凸包就是答案。
void half_plane(){sort(l+1,l+1+tot,cmp);st=1,ed=0;for(int i=1;i<=tot;i++){if(st<=ed&&abs(q[ed].d^l[i].d)<eps) continue;//和上一个共线,必然是无用向量 while(ed-st+1>=2&&out(l[i],pt[ed])) --ed;//先弹队尾 while(ed-st+1>=2&&out(l[i],pt[st+1])) ++st;q[++ed]=l[i];if(ed-st+1>=2) pt[ed]=jiaodian(q[ed-1],q[ed]);}while(ed-st+1>=2&&out(q[st],pt[ed])) --ed;//弹出队尾无用向量 if(ed-st+1>=2) pt[ed+1]=jiaodian(q[st],q[ed]),++ed;//加入队尾与队首的交点 m=ed-st;for(int i=1;i<=m;i++) a[i]=pt[st+i];a[m+1]=a[1];return;
}

细节

  1. 可不可以先弹队首再弹队尾?

不可以!
考虑这两种写法不一样的情况,就是当我判断在半平面外的那个交点恰好就是队首和队尾的交点时
也就是:
在这里插入图片描述
(图片来自:OI-wiki 半平面交)

显然这个时候弹队首会出错。

  1. 如何判断交集为空或无边界?

考虑在正无穷远处加入四条与坐标轴平行的边,把整个平面框成一个大矩形,在进行半平面交算法。
这样就不会出现无边界情况,要判断也可以通过凸包内是否出现了那四条边来进行。
而且,这样之后,凸包元素不足三个就变成了答案无解的充要条件。
非常优美简洁。

代码

P4196 [CQOI2006]凸多边形 /【模板】半平面交

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
inline ll read(){ll x(0),f(1);char c=getchar();while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}return x*f;
}//basic declare
//#define double long double
const double eps=1e-10;
const double pi=acos(-1.0);//---about vectors (or points)//definition
struct V{double x,y;V():x(0),y(0){}V(double a,double b):x(a),y(b){}
};
inline void input(V &a){scanf("%lf%lf",&a.x,&a.y);}
void print(const V &a,int op=1){printf("%g %g",a.x,a.y);putchar(op?10:32);}
//op:endl or space//basic operation
inline V operator + (const V &a,const V &b){return (V){a.x+b.x,a.y+b.y};}
inline V operator - (const V &a,const V &b){return (V){a.x-b.x,a.y-b.y};}
inline V operator * (const double &x,const V &a){return (V){a.x*x,a.y*x};}
inline V operator * (const V &a,const double &x){return (V){a.x*x,a.y*x};}
inline V operator / (const V &a,const double x){return (V){a.x/x,a.y/x};}
inline bool operator == (const V &a,const V &b){return abs(a.x-b.x)<eps&&abs(a.y-b.y)<eps;}
inline bool operator != (const V &a,const V &b){return !(a==b);}
inline double operator * (const V &a,const V &b){return a.x*b.x+a.y*b.y;}
inline double operator ^ (const V &a,const V &b){return a.x*b.y-a.y*b.x;}
inline double len(const V &a){return sqrt(a.x*a.x+a.y*a.y);}
inline V mid(const V &a,const V &b){return (V){(a.x+b.x)/2,(a.y+b.y)/2};}
inline V chui(const V &a){return (V){a.y,-a.x};}//not take direction into account
inline V danwei(const V &a){return a/len(a);}
inline double ang(const V &a,const V &b){return acos((a*b)/len(a)/len(b));}
inline V rotate(const V &o,double t){//COUNTER_CLOCKWISEdouble s=sin(t),c=cos(t);return (V){o.x*c-o.y*s,o.x*s+o.y*c};
}
struct line{V d,a;
};
void print(const line &l,int op=1){printf("point: ");print(l.a,0);printf("dir: ");print(l.d,op);
}
inline line trans(const V &a,const V &b){return (line){b-a,a};
}
inline line trans(const double &a,const double &b,const double &c,const double &d){
//given one point and directionreturn (line){(V){c,d},(V){a,b}};
}
inline V jiaodian(const line &u,const line &v){if(u.d==v.d){print(u);print(v);exit(0);}double k=((v.a-u.a)^v.d)/(u.d^v.d);return u.a+k*u.d;
}inline bool in(const line &l,const V o){return (l.d^(o-l.a))>0;}
inline bool out(const line &l,const V o){return (l.d^(o-l.a))<-eps;}const int N=1005;
int n,m;int tot,st,ed;
V a[N],pt[N];
line l[N],q[N];bool cmp(const line l1,const line l2){double t1=atan2(l1.d.y,l1.d.x),t2=atan2(l2.d.y,l2.d.x);if(abs(t1-t2)>eps) return t1<t2;return in(l2,l1.a);
}const double inf=1e9;
void half_plane(){l[++tot]=trans(0,0,1,0);//插入边界l[++tot]=trans(X,0,0,1);l[++tot]=trans(0,Y,-1,0);l[++tot]=trans(0,0,0,-1);sort(l+1,l+1+tot,cmp);st=1,ed=0;for(int i=1;i<=tot;i++){if(st<=ed&&abs(q[ed].d^l[i].d)<eps) continue;//和上一个共线,必然是无用向量 while(ed-st+1>=2&&out(l[i],pt[ed])) --ed;//先弹队尾 while(ed-st+1>=2&&out(l[i],pt[st+1])) ++st;q[++ed]=l[i];if(ed-st+1>=2) pt[ed]=jiaodian(q[ed-1],q[ed]);}while(ed-st+1>=2&&out(q[st],pt[ed])) --ed;//弹出队尾无用向量 if(ed-st+1>=2) pt[ed+1]=jiaodian(q[st],q[ed]),++ed;//加入队尾与队首的交点 m=ed-st;for(int i=1;i<=m;i++) a[i]=pt[st+i];a[m+1]=a[1];return;
}
signed main(){
#ifndef ONLINE_JUDGEfreopen("a.in","r",stdin);freopen("a.out","w",stdout);
#endifn=read();for(int i=1;i<=n;i++){m=read();for(int j=1;j<=m;j++) input(a[j]);a[m+1]=a[1];for(int j=1;j<=m;j++) l[++tot]=trans(a[j],a[j+1]);}half_plane();if(m<3){printf("0.000");return 0;}double res(0);for(int i=1;i<=m;i++) res+=(a[i]^a[i+1])/2;if(res<0) res=-res;printf("%.3lf\n",res);return 0;
}
/*
4
2 8
4 2
6 5
-8 2
*/

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

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

相关文章

[小技巧]C#中如何为枚举类型添加描述方法

背景在我们的日常开发中&#xff0c;我们会经常使用枚举类型。有时我们只需要显示枚举的值或者枚举值对应名称&#xff0c; 但是在某些场景下&#xff0c;我们可能需要将枚举值显示为不同的字符串。例&#xff1a; 当前我们有如下枚举Level这个枚举有4个可选值B, N, G, VG。 现…

Loj#3320-「CCO 2020」旅行商问题

正题 题目链接:https://loj.ac/p/3320 题目大意 有一张nnn个点的无向完全图&#xff0c;每一条边是红色或者蓝色&#xff0c;对于每个点sss求从这个点出发的一条尽量短的经过所有点的路径。 1≤n≤20001\leq n\leq 20001≤n≤2000 解题思路 显然地猜测一下最短的长度肯定是n…

AcWing 1087. 修剪草坪28

AcWing 1087. 修剪草坪 题意: 有n个数&#xff0c;不能选超过连续的k个数&#xff0c;问所能选的最大值是多少&#xff1f; 题解&#xff1a; 我们首先分析dp过程&#xff1a; dp[i]表示选择完前i个数的最大值 sum[i]表示前i项和 对于第i个数&#xff0c;它有两个情况&#…

工业通信的开源项目 HslCommunication 介绍

前言&#xff1a;本项目的孵化说来也是机缘巧合的事&#xff0c;本人于13年大学毕业后去了一家大型的国企工作&#xff0c;慢慢的走上了工业软件&#xff0c;上位机软件开发的道路。于14年正式开发基于windows的软件&#xff0c;当时可选的技术栈就是MFC和C#的winform&#xff…

【地狱副本】数据结构之线段树Ⅲ——区间最值/赋值/修改/历史值操作(HDU5306,Tyvj 1518,【清华集训2015】V,HDU6315,HDU1828,POJ3162)

文章目录Gorgeous SequenceTyvj 1518 CPU监控【清华集训2015】VNaive OperationsPictureWalking RaceGorgeous Sequence HDU5306 操作 区间与xxx取min\rm minmin查询区间最大值查询区间和 比较暴力的线段树维护区间 Max : 区间最大值sub_max : 严格小于最大值的区间次大值…

Acwing 1089. 烽火传递

Acwing 1089. 烽火传递 题意&#xff1a; 有n个数&#xff0c;要保证每m个数中必须选一个&#xff0c;问所选数的最小总和是多少 题解&#xff1a; 我一开始设的状态为:dp[i]表示前i个数选完的最小值&#xff0c;第i个数可以选也可以不选&#xff0c;但是这样一个状态&…

IIS作为ASP.NET Core2.1 反向代理服务器未说的秘密

--以下内容针对 ASP.NET Core2.1&#xff0c;2.2出现IIS进程内寄宿 暂不展开讨论---相比ASP.NET&#xff0c;出现了3个新的组件:ASP.NET Core Module、Kestrel、dotnet.exe&#xff0c; 后面我们会理清楚这三个组件的作用和组件之间的交互原理。 ASP.NET Core 设计的初衷是开源…

程序员修神之路--分布式缓存的一条明路(附代码)

菜菜呀&#xff0c;由于公司业务不断扩大&#xff0c;线上分布式缓存服务器扛不住了呀程序员主力 Y总如果加硬件能解决的问题&#xff0c;那就不需要修改程序菜菜我是想加服务器来解决这个问题&#xff0c;但是有个问题呀程序员主力 Y总&#xff1f;&#xff1f;&#xff1f;菜…

长沙.NET技术社区正式成立

感谢大家的关注&#xff0c;请允许我冒昧的向大家汇报长沙.NET技术社区第一次交流会的会议进展情况。活动过程汇报2019年2月17日&#xff0c;继深圳&#xff0c;广州&#xff0c;西安&#xff0c;成都&#xff0c;苏州相继成立了.net社区之后&#xff0c;酝酿已久的长沙.net社区…

Asp.NetCore轻松学-部署到 IIS 进行托管

前言经过一段时间的学习&#xff0c;终于来到了部署服务这个环节&#xff0c;.NetCore 的部署方式非常的灵活多样&#xff0c;但是其万变不离其宗&#xff0c;所有的 Asp.NetCore 程序都基于端口的侦听&#xff0c;在部署的时候仅需要配置侦听地址、端口&#xff08;一个或者多…

响应式编程知多少 | Rx.NET 了解下

1. 引言An API for asynchronous programming with observable streams. ReactiveX is a combination of the best ideas from the Observer pattern, the Iterator pattern, and functional programming.ReactiveX 使用可观察数据流进行异步编程的API。 ReactiveX结合了观察者…

.NET Core中的验证组件FluentValidation的实战分享

今天有人问我能不能出一篇FluentValidation的教程&#xff0c;刚好今天在实现我们的.NET Core实战项目之CMS的修改密码部分的功能中有用到FluentValidation&#xff0c;所以就以修改用户密码为实例来为大家进行一下ASP.NET Core中的验证组件FluentValidation的实战分享&#xf…

笛卡尔树详解带建树模板及例题运用(Largest Submatrix of All 1’s,洗车 Myjnie,Removing Blocks,SPOJ PERIODNI)

文章目录笛卡尔树介绍例题Largest Submatrix of All 1’s应用「POI2015」洗车 Myjnie[AGC028B] Removing BlocksSPOJ PERIODNI笛卡尔树 介绍 笛卡尔树是一种数据结构&#xff0c;每个点由两个值&#xff0c;键值key和权值val&#xff0c;组成 其键值满足二叉树性质 即点的左子…

如何为ASP.NET Core设置客户端IP白名单验证

本篇博文中展示了如何在ASP.NET Core应用程序中设置IP白名单验证的3种方式。你可以使用一下3种方式&#xff1a;使用中间件检查每个请求的远程IP地址使用Action过滤器为指定的Controller或action方法添加针对远程IP地址的检查使用IPageFilter为Razor Pages应用添加针对远程IP地…

让ASP.NET Core支持GraphQL之-GraphQL的实现原理

众所周知RESTful API是目前最流行的软件架构风格之一&#xff0c;它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁&#xff0c;更有层次&#xff0c;更易于实现缓存等机制。RESTful的优越性是毋庸置疑的&#xff0c;不过GraphQL也可以作为一种补充&am…

[2021-09-02 contest]CF1251C,可达性统计(bitset优化dp),Boomerang Tournament(状压dp),小蓝的好友(mrx)(treap平衡树)

文章目录CF1251C Minimize The Integeracwing164&#xff1a;可达性统计Facebook Hacker Cup 2016 Round 1 Boomerang Tournament[Zjoi2012]小蓝的好友(mrx)CF1251C Minimize The Integer ………………… 给你一个大整数aaa&#xff0c;它由nnn位数字&#xff0c;也可能有前导…

Entity Framework 的一些性能建议

点击上方蓝字关注“汪宇杰博客”这是一篇我在2012年写的老文章&#xff0c;至今适用&#xff08;没错&#xff0c;我说的就是适用于EF Core&#xff09;。因此使用微信重新推送&#xff0c;希望能帮到大家。自从我用了EF&#xff0c;每次都很关心是否有潜在的性能问题。所以每次…

微软内部全面拥抱开源流程Inner Source

微软过去几年一直是 GitHub 平台上开源贡献者最多的公司。不仅如此&#xff0c;微软还将继续拥抱开源&#xff0c;内部有一项名为 Inner Source 的计划&#xff0c;将开源开发流程引入到公司内部。事实上&#xff0c;Inner Source 已经存在于微软内部多年&#xff0c;包括更多代…

k8s使用helm打包chart并上传到腾讯云TencentHub

本文只涉及Helm的Chart操作&#xff0c;不会对其他知识进行过多描述。至于安装这块&#xff0c;麻烦自行百度吧&#xff0c;一大堆呢。在容器化的时代&#xff0c;我们很多应用都可以部署在docker&#xff0c;很方便&#xff0c;而再进一步&#xff0c;我们还有工具可以对docke…

数据结构之基环树——骑士,Island,旅行加强版,Number of Simple Paths,Traffic Network in Numazu,Card Game

文章目录[ZJOI2008]骑士[IOI2008] Island[NOIP2018 提高组] 旅行 加强版CF1454E Number of Simple PathsTraffic Network in NumazuCard Game基环树的常见解法若干个基环树互相独立断环为链&#xff08;随便断一条&#xff09;环外树和环外树之间的树形DP环变链后整体可以用数据…