计算几何-半平面交
作者:互联网
计算几何-半平面交
半平面
平面内的一条直线把这个平面分成两部分,每一部分对这个平面来说,都叫做半平面。包括这条直线的半平面叫做闭半平面,否则叫做开半平面。
解析式为 \(Ax + By +C >=0\)或\(Ax + By +C <=0\)。
在计算几何中用向量表示,整个题统一以向量的左侧或右侧为半平面。
半平面交
半平面交就是多个半平面的交集。半平面交是一个点集。
它可以理解为向量集中每一个向量的右侧的交,或者是下面方程组的解。
\[ \begin{equation} \begin{cases} A1x+B1y+C1\ge0\newline A2x+B2y+C2\ge0\newline ~~~~~~~~~~~~~~\dots \end{cases} \end{equation} \]多边形的核
如果一个点集中的点与多边形上任意一点的连线与多边形没有其他交点,那么这个点集被称为多边形的核。
把多边形的每条边看成是首尾相连的向量,那么这些向量在多边形内部方向的半平面交就是多边形的核。
求法
D&C算法
该算法是基于分治思想的:
-
将\(n\)个半平面分成两个\(n/2\)的集合;
-
对两个子集和递归求解半平面交;
-
将前一步算出来的两个交利用平面扫描法求解。
时间复杂度\((n \log n)\)这个算法并不常用,主要介绍的是下面这个。
S&I算法
该算法是在2006年有中国队队员朱泽园提出来的“排序增量法”。
假设给出\(n\)条直线,求这\(n\)条直线的左方半平面的交集:
-
首先对这\(n\)条直线按极角排序;
-
用一个队列去维护半平面的交集,和相邻两条直线的交点;
-
每次加入新的直线时判断是否有交点在该直线的右面,如果是则弹出直线,先判队尾再判队首,注意判断平行情况;
-
最后队列中的交集即为半平面交。
Code:
#include<bits/stdc++.h>
#define eps 1e-8
using namespace std;
const int maxn=1010;
struct geometric{
double x,y;
geometric(double X=0,double Y=0):x(X),y(Y) {}
friend geometric operator + (const geometric a,const geometric b){return geometric(a.x+b.x,a.y+b.y);}
friend geometric operator - (const geometric a,const geometric b){return geometric(a.x-b.x,a.y-b.y);}
friend geometric operator * (const geometric a,double p){return geometric(a.x*p,a.y*p);}
friend geometric operator / (const geometric a,double p){return geometric(a.x/p,a.y/p);}
double dis(geometric a,geometric b){return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));}
double dot(geometric a1,geometric a2,geometric b1,geometric b2){return (a2.x-a1.x)*(b2.x-b1.x)+(a2.y-a1.y)*(b2.y-b1.y);}
double cross(geometric a1,geometric a2,geometric b1,geometric b2){return (a2.x-a1.x)*(b2.y-b1.y)-(a2.y-a1.y)*(b2.x-b1.x);}
double corner(geometric a1,geometric a2,geometric b1,geometric b2){return dot(a1,a1,b1,b2)/(dis(a1,a2)*dis(b1,b2));}
double area(geometric a1,geometric a2,geometric b1,geometric b2){return fabs(cross(a1,a2,b1,b2));}
double angle(geometric a){return atan2(a.y,a.x);}
}opt;
int n,m,tot,head=1,tail=1;double ans;
geometric data[maxn],origin,T[maxn];
struct line{
geometric A,B;double An;
line(geometric a,geometric b):A(a),B(b) {An=opt.angle(B);}
line(){}
bool operator < (const line &a)const{return An<a.An;}
geometric sdot(line a,line b){
geometric c=a.A-b.A;
double k=opt.cross(origin,b.B,origin,c)/opt.cross(origin,a.B,origin,b.B);
return a.A+a.B*k;
}
}q[maxn],p[maxn],take;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&m);
for(int j=1;j<=m;j++)
scanf("%lf%lf",&data[j].x,&data[j].y);
for(int j=1;j<=m;j++)
{
if(j==m)p[++tot]=line(data[j],data[1]-data[j]);
else p[++tot]=line(data[j],data[j+1]-data[j]);
}
}
sort(p+1,p+tot+1);
q[head]=p[head];
for(int i=2;i<=tot;i++)
{
while(head<tail&&opt.cross(origin,p[i].B,p[i].A,T[tail-1])<=eps)tail--;
while(head<tail&&opt.cross(origin,p[i].B,p[i].A,T[head])<=eps)head++;
q[++tail]=p[i];
if(fabs(opt.cross(origin,q[tail].B,origin,q[tail-1].B))<=eps)
{
tail--;
if(opt.cross(origin,q[tail].B,q[tail].A,p[i].A)>eps)q[tail]=p[i];
}
if(head<tail)T[tail-1]=take.sdot(q[tail-1],q[tail]);
}
while(head<tail&&opt.cross(origin,q[head].B,q[head].A,T[tail-1])<=eps)tail--;
if(tail-head>1)
T[tail]=take.sdot(q[head],q[tail]);
for(int i=head;i<=tail;i++)
{
if(i==tail)ans+=opt.cross(origin,T[i],origin,T[head]);
else ans+=opt.cross(origin,T[i],origin,T[i+1]);
}
printf("%.3lf",ans/2);
return 0;
}
一些例题
标签:b2,double,a1,b1,计算,几何,平面,geometric 来源: https://www.cnblogs.com/Jekyll-Y/p/16324580.html