1. 程式人生 > 其它 >半平面交

半平面交

半平面交

定義:

半平面:

顧名思義,就是平面的一半。一條直線會把平面分成兩部分,就是兩個半平面。對於半平面,我們可以用直線方程式如:\(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;
}