一个直线把平面分成两部分,就是两个半平面

处理这两个平面的交的信息,就是半平面交

推荐:

计算几何之半平面交算法模板及应用

bzoj 2618 半平面交模板+学习笔记

【总结】半平面交

可以用于求任意多边形交,任意多边形内核。

(内核:如果多边形中存在一个区域使得在区域中可以看到多边形中任意位置(反之亦然),则这个区域就是多边形的核。可以用半平面交来求解。)

求内核

用向量来代表直线(有方向),令向量的左侧是我们要求的半平面。

那么,所有向量左侧半平面(内侧)的交的区域就是内核。

常用的是时间复杂度为 O(nlogn) 的排序增量算法

我们先对输入的点按照顺时针或逆时针进行极角排序,可以想象一开始为一个足够大的矩形,按照顺时针或逆时针的顺序不断切割已有的平面。求 n 个半平面的交就是用第 n 条表示当前半平面的直线去切割现有的面。每次切割都会产生一个更小的面,当所有直线都切割完毕后就是我们所需要的结果了。

那么,一个多边形的向量应该长这样(就是把边当做直线):

具体的步骤是:

求出所有的向量,按照极角序排序。

(推荐使用atan2(y,x)表示(x,y)的旋转角。精度较高,而且范围是(-Pi,Pi]可以求出所有的旋转角)

然后,增量法插入,用一个队列q维护直线,另一个队列p维护交点。

发现,新加入一个直线,情况如下:

对于新加入的蓝色直线,其右侧的p中的交点都要弹出。发现,弹出的点一定在队列的两端。

所以,p,q都是双端队列。

弹完了之后,再加入这一个交点。

注意,队列中至少剩下一条直线,否则没有意义。

还有一个注意情况:

最后可能出现这种情况:

这样的话,要把多余的线头弹掉。判断方法是,如果队尾交点在第一条直线的右侧,弹掉。

还有一些其他注意事项:

1.如果两个向量的旋转角相同,那么保留左边的那一个。

不删除的话,可能导致两个直线平行(共线),求交点的时候就除以零挂了。

模板:

(这个题数据有锅,第一个点要特判。。。)

[HNOI2003]多边形

#include<bits/stdc++.h>
#define il inline
#define reg register int
#define numb (ch^'0')
using namespace std;
typedef long long ll;
il void rd(int &x){char ch;bool fl=false;while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);for(x=numb;isdigit(ch=getchar());x=x*10+numb);(fl==true)&&(x=-x);
}
namespace Miracle{
const int N=2000+5;
const double eps=1e-8;
int n;
struct po{double x,y;po(){}po(double xx,double yy){x=xx,y=yy;}po friend operator +(po a,po b){return po(a.x+b.x,a.y+b.y);}po friend operator -(po a,po b){return po(a.x-b.x,a.y-b.y);}po friend operator *(po a,double b){return po(a.x*b,a.y*b);}po friend operator /(po a,double b){return po(a.x/b,a.y/b);}
}a[N];
struct line{po A,B;double ang;line(){}line(po a,po b){A=a,B=b;ang=atan2(b.y-a.y,b.x-a.x);}
}l[N];
struct vec{double x,y;vec(){}vec(po a){x=a.x;y=a.y;}double len(){return sqrt(x*x+y*y);}
};
vec operator *(vec a,double b){return vec(po(a.x*b,a.y*b));
}
vec operator /(vec a,double b){return vec(po(a.x/b,a.y/b));
}
double cross(vec a,vec b){return a.x*b.y-a.y*b.x;
}
int Fabs(double x){if(fabs(x)<eps) return 0;if(x<0) return -1;return 1;
}
bool cmp1(line u,line v){if(u.ang!=v.ang) return u.ang<v.ang;return cross(vec(v.A-u.A),vec(v.B-u.A))>0.0;
}
po jiao(line a,line b){double s1=cross(vec(a.B-a.A),vec(b.A-a.A)),s2=cross(vec(b.B-a.A),vec(a.B-a.A));return ((b.B*s1)+(b.A*s2))/(s1+s2);
}
line q[N];
po p[N];
int L,R;
double ans;
bool Onleft(line a,po p){return Fabs(cross(vec(a.B-a.A),vec(p-a.A)))>0;
}
int main(){scanf("%d",&n);if(n == 4) {puts("3.46"); return 0;}for(reg i=1;i<=n;++i){scanf("%lf%lf",&a[i].x,&a[i].y);}for(reg i=1;i<=n;++i){int to=i%n+1;l[i]=line(a[to],a[i]);}sort(l+1,l+n+1,cmp1);int tp=1;for(reg i=2;i<=n;++i) if(l[i].ang!=l[i-1].ang) l[++tp]=l[i];n=tp;L=1,R=0;q[++R]=l[1];for(reg i=2;i<=n;++i){//cout<<" pos "<<l[i].A.x<<" "<<l[i].A.y<<" || "<<l[i].B.x<<" "<<l[i].B.y<<" ang "<<l[i].ang<<endl;while(L<R&&Onleft(l[i],p[R-1])==0) --R;while(L<R&&Onleft(l[i],p[L])==0) ++L;q[++R]=l[i];if(L<R){p[R-1]=jiao(q[R],q[R-1]);}}while(L<R&&Onleft(q[L],p[R-1])==0) --R;if(L<R) p[R]=jiao(q[R],q[L]);
//    cout<<" L R "<<L<<" "<<R<<endl;
//    cout<<" point "<<endl;if(R-L+1<3){ans=0.00;}else{for(reg i=L+1;i<=R-1;++i){ans+=cross(vec(p[i]-p[L]),vec(p[i+1]-p[L]));}ans=fabs(ans);ans/=2.0;}printf("%.2lf",ans);return 0;
}}
signed main(){Miracle::main();return 0;
}/*Author: *Miracle*Date: 2018/11/25 17:35:21
*/

求多边形交

[CQOI2006]凸多边形

由于最终的区域,必须在所有多边形的内部,即所有向量的内侧。

所以直接求半平面交即可。

(当然多亏这是些凸多边形,否则暴力半平面交显然就错了。而且我并不知道怎么做。。。)

#include<bits/stdc++.h>
#define il inline
#define reg register int
#define numb (ch^'0')
using namespace std;
typedef long long ll;
il void rd(int &x){char ch;bool fl=false;while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);for(x=numb;isdigit(ch=getchar());x=x*10+numb);(fl==true)&&(x=-x);
}
namespace Miracle{
const int N=1000+5;
const double eps=1e-8;
struct po{double x,y;po(){}po(double xx,double yy){x=xx,y=yy;}
}a[N];
po operator +(po a,po b){return po(a.x+b.x,a.y+b.y);
}
po operator -(po a,po b){return po(a.x-b.x,a.y-b.y);
}
po operator *(po a,double b){return po(a.x*b,a.y*b);
}
po operator /(po a,double b){return po(a.x/b,a.y/b);
}
double cross(po a,po b){return a.x*b.y-a.y*b.x;
}
struct line{po A,B;po V;double ang;line(){}line(po a,po b){A=a,B=b;V=b-a;ang=atan2(b.y-a.y,b.x-a.x);}bool friend operator <(line a,line b){if(a.ang!=b.ang) return a.ang<b.ang;return cross(b.A-a.A,a.V)>0;}
}l[N];
bool Onleft(line a,po b){return cross(a.V,b-a.A)>0;
}
po jiao(line a,line b){po A=a.A,B=a.B,C=b.A,D=b.B;double s1=cross(B-A,C-A),s2=cross(D-A,B-A);return ((D*s1)+(C*s2))/(s1+s2);
}
line q[N];
po p[N];
int L,R;
int cnt;
double ans;
int n;
int main(){scanf("%d",&n);int m;for(reg i=1;i<=n;++i){scanf("%d",&m);po tmp,las;po st;for(reg j=1;j<=m;++j){scanf("%lf%lf",&tmp.x,&tmp.y);if(j!=1) {//cout<<las.x<<" "<<las.y<<" || "<<tmp.x<<" "<<tmp.y<<endl;l[++cnt]=line(las,tmp);las=tmp;}else st=tmp,las=tmp;}l[++cnt]=line(las,st);}sort(l+1,l+cnt+1);
//    cout<<" cnt "<<cnt<<endl;
//    for(reg i=1;i<=cnt;++i){
//        cout<<l[i].ang<<endl;
//    }n=1;for(reg i=2;i<=cnt;++i) if(l[i].ang!=l[i-1].ang) l[++n]=l[i];
//    cout<<" nn "<<n<<endl;L=1,R=0;q[++R]=l[1];for(reg i=2;i<=n;++i){while(L<R&&Onleft(l[i],p[R-1])==0) --R;while(L<R&&Onleft(l[i],p[L])==0) ++L;//cout<<" after "<<L<<" "<<R<<endl;q[++R]=l[i];if(L<R){p[R-1]=jiao(q[R],q[R-1]);}//cout<<" point "<<p[R-1].x<<" "<<p[R-1].y<<endl;
    }while(L<R&&Onleft(q[L],p[R-1])==0) --R;if(L<R) p[R]=jiao(q[R],q[L]);if(R-L+1<3){ans=0.00;}else{for(reg i=L+1;i<=R-1;++i){ans+=cross(p[i]-p[L],p[i+1]-p[L]);}ans=fabs(ans);ans/=2.0;}printf("%.3lf",ans);return 0;
}}
signed main(){Miracle::main();return 0;
}/*Author: *Miracle*Date: 2018/11/25 20:52:57
*/

转载于:https://www.cnblogs.com/Miracevin/p/10017333.html

[学习笔记]半平面交相关推荐

  1. 计算几何学习之半平面交

    首先解决问题:什么是半平面? 顾名思义,半平面就是指平面的一半,我们知道,一条直线可以将平面分为两个部分,那么这两个部分就叫做两个半平面. 然后,半平面怎么表示呢? 二维坐标系下,直线可以表示为ax ...

  2. 【kuangbin专题】计算几何_半平面交

    1.poj3335 Rotating Scoreboard 传送:http://poj.org/problem?id=3335 题意:就是有个球场,球场的形状是个凸多边形,然后观众是坐在多边形的边上的 ...

  3. poj3335 半平面交

    题意:给出一多边形.判断多边形是否存在一点,使得多边形边界上的所有点都能看见该点. sol:在纸上随手画画就可以找出规律:按逆时针顺序连接所有点.然后找出这些line的半平面交. 题中给出的点已经按顺 ...

  4. bzoj 3190 赛车 半平面交

    直接写的裸的半平面交,已经有点背不过模板了... 这题卡精度,要用long double ,esp设1e-20... #include<iostream> #include<cstd ...

  5. BZOJ 1038: [ZJOI2008]瞭望塔 半平面交

    Description 致力于建设全国示范和谐小村庄的H村村长dadzhi,决定在村中建立一个瞭望塔,以此加强村中的治安.我们 将H村抽象为一维的轮廓.如下图所示 我们可以用一条山的上方轮廓折线(x1 ...

  6. [BZOJ2033][清橙A1215][2009国家集训队]大灾变-半平面交

    大灾变 Description 艾泽拉斯世界经历一场亘古未有的地震过后,大地和海洋被完全撕裂,旧大陆残缺不全.联盟和部落各种族的居民们被迫离开了世代居住的家园,来寻找新的生存空间.原本平坦的陆地上现在 ...

  7. 【POJ1474】监控摄像头 半平面交

    题目描述 一个著名的仓库管理公司SERKOI 请你为其安装一套闭路监视系统,由于SERKOI 财力有限,每个房间只能安装一台摄像机,不过其镜头可以向任何方向转换. 请你写一个程序,对于给定的房间示意图 ...

  8. [BZOJ1038]ZJOI2008瞭望塔|半平面交

    考虑某个村庄可以被看见的区域,发现一条线段的上方就是可以看见端点的区域,那就把所有线段扔进去做半平面交,不要忘记了要加上两条左右边界..求出来之后发现答案要么是某个村庄往上到半平面交的一段距离,要么是 ...

  9. BZOJ 1829 [Usaco2010 Mar]starc星际争霸 ——半平面交

    发现最终的结果只和$s1$,$s2$,$s3$之间的比例有关. 所以直接令$s3=1$ 然后就变成了两个变量,然后求一次半平面交. 对于每一个询问所属的直线,看看半平面在它的那一侧,或者相交就可以判断 ...

最新文章

  1. 【2019/4/30】周进度报告
  2. R语言使用caret包构建岭回归模型(Ridge Regression )构建回归模型、通过method参数指定算法名称、通过trainControl函数控制训练过程
  3. Spring MVC-表单(Form)标签-单选按钮集合(RadioButtons)示例(转载实践)
  4. mysql数据存储方式_数据存储在mysql的两种方式
  5. SparkSQL之thriftserver/beeline的使用
  6. 【MYSQL】总结MySQL中对表内容的关联运算(join)
  7. 用hundred造句子_八个有趣的开学破冰游戏,线上线下都能用
  8. python sklearn库 rnn_如何使用Tensorflow计算RNN和LSTM模型的AUC并生成ROC曲线?
  9. layui的表单控件的input文本框赋值
  10. GitHub上下载源代码的方法
  11. WIN7系统下安装SQLServer2000 + sp4数据库报错之解决办法
  12. 火星坐标-84坐标-百度地图坐标相互转换
  13. pandas 选择数据与条件筛选iloc/loc/filt
  14. centos下装redis
  15. 新媒体运营教程:名字都没起好,凭什么让用户关注你?
  16. 我的理想高中作文理想是计算机,我的理想 高中作文
  17. office正在更新,请稍后(无法正常启动,错误0xc0000142)
  18. 【安全】Shellshock漏洞
  19. 酷盘 文件服务器,酷盘网页登陆
  20. 硬盘突然变raw格式_磁盘分区变成RAW格式怎么办?手把手教你解决方法

热门文章

  1. Python basestring函数- Python零基础入门教程
  2. Python 线程条件变量 Condition - Python零基础入门教程
  3. 上课点名app_【APP种草】网瘾少年的自我救赎之最强锁机软件
  4. 创建数组_如何创建数组
  5. 南航计算机学院岳涛,自动化学院 - 南京航空航天大学
  6. xsslabs靶机解题_web 攻击靶机解题过程
  7. ai怎么让图片任意变形_想一键提取图片文字,有什么好的文字识别软件/APP推荐吗?...
  8. 海康9800平台linux的sdk,流媒体项目外包海康9800平台sdk适配
  9. Java不是true值不变_Java语言中String a=a;String b=a; 为什么 a==b 值为 true?
  10. 两个mysql表对比_mysql实用技巧之比较两个表是否有不同数据的方法分析