半平面交

半平面交

定义:

半平面:

顾名思义,就是平面的一半。一条直线会把平面分成两部分,就是两个半平面。对于半平面,我们可以用直线方程式如:\(ax+by>=c\) 表示,更常用的是用直线表示。

半平面交:

顾名思义,就是多个半平面求交集。其结果可能是一个凸多边形、无穷平面、直线、线段、点等。

多边形的核:

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

极点:

\((x,y)\) 与原点的连线与 \(x\) 轴的夹角,其范围为 \([0,360]\).

算法流程:

  1. 将所有直线极角排序,角度相同的保留下需要的一个

  2. 用一个双端队列存储当前半平面交,每次通过判断队首与队尾第一个交点是否满足当前直线来更新

  3. 先用队尾判定队首交点是否合法,再用队首判断队尾交点是否合法

  4. 最后求出来的半平面交是一个凸多边形

同时,算法的最后需要删去不合法的点。

例题:

P4196 [CQOI2006]凸多边形

题解都在注释中:

#include<bits/stdc++.h>
using namespace std;
#define ld double 
const ld eps=1e-8,PI=acos(-1);
const int N=2e5+5;
int n,m,sum; int L=1,R=1;
inline int dcmp(ld x){return x<-eps?-1:(x>eps?1:0);}
struct Point{
    ld x,y; Point(ld X=0,ld Y=0){x=X,y=Y;}
}p[N],Q[N],s[N];
inline Point operator+(Point a,Point b){return Point(a.x+b.x,a.y+b.y);}
inline Point operator-(Point a,Point b){return Point(a.x-b.x,a.y-b.y);}
inline Point operator*(Point a,ld b){return Point(a.x*b,a.y*b);}
inline bool operator==(Point a,Point b){//两点坐标重合
    return !dcmp(a.x-b.x)&&dcmp(a.y-b.y);
}
struct Line{
    Point s,t; ld ang;
    Line(Point X=Point(0,0),Point Y=Point(0,0)){s=X,t=Y,ang=atan2(t.y,t.x);}
    bool operator<(const Line&a) const{return ang<a.ang;}
}l[N],que[N];

inline ld Dot(Point a,Point b){return a.x*b.x+a.y*b.y;}//点积(数量积)
inline ld Cro(Point a,Point b){return a.x*b.y-a.y*b.x;}//叉积
bool is_para(Line a,Line b){return dcmp(Cro(a.t-a.s,b.t-b.s))==0;}
Point intersection(Line a,Line b){//交点
    return a.s+a.t*(Cro(b.t,a.s-b.s)/Cro(a.t,b.t));
}
bool OnRight(Line a,Point b){return dcmp(Cro(a.t,b-a.s))<0;}

void Si(){
    sort(l+1,l+1+sum); que[1]=l[1];
    for(int i=2;i<=sum;i++){
		while(L<R&&OnRight(l[i],p[R-1])) R--;//踢出队尾,交点在当前边右侧
		while(L<R&&OnRight(l[i],p[L]))  L++;//踢出队首,交点在当前边右侧
		que[++R]=l[i];
        if(fabs(Cro(que[R].t,que[R-1].t))<=eps){
			R--;
			if(Cro(que[R].t,l[i].s-que[R].s)>eps) que[R]=l[i];//取更左边那个
		}
		if(L<R) p[R-1]=intersection(que[R],que[R-1]);//有多个边在集合中,加入交点
    }
    while(L<R&&OnRight(que[L],p[R-1])) R--;//删去多余的点
    if(R-L<=1) return;//只有一条边
    p[R]=intersection(que[L],que[R]);//加入最后一条边
}
ld area(){
    double res=0; 
    for(int i=L;i<=R;i++){
        int y=i+1; if(i==R) y=L;
        res+=Cro(p[i],p[y]);
    }
    return fabs(res/2);
}
double solve(){
    Si();
    return area();
}
int main(){
    int cnt=0; cin>>cnt;
    while(cnt--){
        cin>>n; for(int j=1;j<=n;j++) scanf("%lf%lf",&Q[j].x,&Q[j].y);
        for(int j=1;j<=n;j++){
            int to=j+1; if(j==n) to=1;
            l[++sum]=Line(Q[j],Q[to]-Q[j]);
        }
    }
    printf("%.3lf\n",solve());
    system("pause");
    return 0;
}

posted @ 2021-08-05 18:55  Evitagen  阅读(153)  评论(0编辑  收藏  举报