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

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

(逃)

前言

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

解析

半平面的表示

首先,我们需要一个简洁的方式表达半平面。
不禁想到,我们可以利用叉积的符号来判断点是在一条线的顺时针还是逆时针方向。那么类似的,我们可以用一个点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,一经查实,立即删除!

相关文章

Codeforces Round #740 (Div. 2, based on VK Cup 2021 - Final (Engine)) A-F全题解

Codeforces Round #740 (Div. 2, based on VK Cup 2021 - Final (Engine)) 文章目录A. Simply Strange SortB. Charmed by the GameC. Deep Down BelowD1/D2. Up the StripE. Bottom-Tier ReversalsF. Top-Notch InsertionsA. Simply Strange Sort 签到题&#xff0c;暴力做 …

[小技巧]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;它有两个情况&#…

洛谷P4463:calc(dp、拉格朗日插值)

Solution\text{Solution}Solution 神奇题目。 首先可以强制所有的数递增&#xff0c;最后的答案乘一个 n!n!n! 即可。 设 dpi,jdp_{i,j}dpi,j​ 表示在 [1,j][1,j][1,j] 的值域选了 iii 个数的答案&#xff0c;不难写出 dp 转移&#xff1a; dpi,jdpi−1,j−1jdpi,j−1dp_{i,j…

CF1710C-XOR Triangle【dp】

正题 题目链接:https://www.luogu.com.cn/problem/CF1710C 题目大意 求有多少对0≤a,b,c≤n0\leq a,b,c\leq n0≤a,b,c≤n满足axorb,axorc,bxorca\ xor\ b,a\ xor\ c,b\ xor\ ca xor b,a xor c,b xor c作为边长时能构成一个非退化三角形。 n以二进制形式给出 1≤n<221051…

工业通信的开源项目 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;但是这样一个状态&…

CF886E Maximum Element(dp、组合数学)

Solution\text{Solution}Solution 纯纯的dp题。 关键在于我们 dp 时只关注不同元素之间的相对大小。 非法情况不易统计&#xff0c;考虑转而考虑合法情况再用全排列减。 设计 fif_ifi​ 为长度为 iii 的排列循环到一直最后也没有跳出的方案数。 枚举最大的元素 iii 放置的位置…

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 设计的初衷是开源…

CF1710B-Rain【堆】

正题 题目链接:https://www.luogu.com.cn/problem/CF1710B 题目大意 一个数轴&#xff0c;每个位置上开始时都有一个ax0a_x0ax​0&#xff0c;nnn次操作pi,hip_i,h_ipi​,hi​对于所有位置axa_xax​令其变为axmax⁡(hi−abs(pi−x),0)a_x\max(h_i-abs(p_i-x),0)ax​max(hi​−…

数据结构之线段树Ⅴ——(李超线段树)Robot,Product Sum,Building Bridges,Jump mission

文章目录RobotProduct SumBuilding BridgesJump missionRobot BZOJ3938 机器人每次一旦改变速度&#xff0c;直到下一次改变速度为止 这一时间段内机器人的位置下标可以用一次函数表示 如果知道时刻t1t_1t1​即将改变速度的机器人位置&#xff0c;以及最近的下一次机器人速…

模板:拉格朗日插值(多项式)

所谓拉格朗日插值&#xff0c;就是在“拉格朗日”进行的一项民俗活动。拉格朗日通常在每年2月的第82个星期三。 &#xff08;逃&#xff09; 前言 非常强大的算法。 当可以证明某个函数是一个 kkk 次多项式时&#xff0c;我们就可以插入 k1k1k1 个函数值并快速的求出我们要求…

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

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

2019 ICPC Asia Nanjing Regional

题号题目难度知识点AA Hard Problem签到题思维题BChessboardCDigital Path签到题dfs记忆化搜索DHolesEObservationFPaper GradingGPoker GameHPrince and Princess四稳铜快银思维题ISpace StationJSpyKTriangle三题快铜计算几何

uoj#751-[UNR #6]神隐【交互】

正题 题目链接:https://uoj.ac/problem/751 题目大意 有一棵nnn个点的树&#xff0c;你每次可以选择一个边集&#xff0c;交互库会返回你所有联通块&#xff0c;要求这棵树。 n≤2000n\leq 2000n≤2000&#xff0c;操作次数不超过141414。 或 n≤131072n\leq 131072n≤131072…

模板:快速莫比乌斯变换(FMT)+快速沃尔什变换(FWT)(多项式)

文章目录前言解析OR定义变换&#xff1a;逆变换代码AND代码XOR定义变换逆变换代码所谓快速沃尔什变换&#xff0c;就是快速的沃尔玛什锦专柜变换 &#xff08;逃&#xff09; 前言 正常卷积的定义&#xff1a;ck∑ijkaibjc_k\sum_{ijk}a_ib_jck​∑ijk​ai​bj​。 可以用FFT…

[AtCoder Beginner Contest 216] 题解

文章目录A - Signed DifficultyB - Same NameC - Many BallsD - Pair of BallsE - Amusement ParkF - Max Sum CountingG - 01Sequence比赛链接A - Signed Difficulty 签到题 #include <cstdio> int x, y; char c; int main() {scanf( "%d%c%d", &x, &am…

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

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