定义
半平面:平面上的直线及其一侧的部分,可以用 Ax+By+c≥0 A x + B y + c ≥ 0 表示。
在一个有界区域里半平面或半平面的交是一个凸多边形区域。
n个半平面的交是一个至多n条边的凸多边形。
其实半平面交就是线性规划方程组的解集。
举个例子,定义有向直线的右边为半平面,那么半平面交就是下图的红色区域。
求解
可以发现半平面交是一个凸多边形(当然也有是一个点/一条线/空集的情况)。当半平面交无界时,通常再加四个半平面控制边界(就像框了一个矩形一样)。
求解半平面交和凸包有些类似,其大致步骤如下:
1.把所有半平面按照和x轴的夹角排序,在x轴下方的为负。当夹角相同时保留限制最紧的那个。
2.类似凸包的单调栈,维护一个单调队列,假设我们现在新加了一个半平面
l
l
。
3.当队尾的两条直线的交点在的右边时,队尾要被弹掉。如这张图,红色的为
l
l
,为要弹掉的半平面。因为之前排过序,保证了弹掉的一定是队尾。队首也这么做一遍。
4.因为半平面交是首尾相接的,所以后面会有一些无用的半平面。这时就要把队首插入队尾维护一遍。
5.最后队列里剩下的就是解了。
模板
以BZOJ2618为例:
有一个计算凸多边形的面积公式:
2S=∑ni=1ai→×ai+1→+a1→×an→ 2 S = ∑ i = 1 n a i → × a i + 1 → + a 1 → × a n →
代码:
#include<cmath>
#include<cctype>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 1005
#define F inline
using namespace std;
typedef double DB;
struct P{ DB x,y; }a[N],tmp[N];
struct L{ P a,b; DB ag; }t[N],q[N];
int n,k,nd; DB ans;
F char readc(){
static char buf[100000],*l=buf,*r=buf;
if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
if (l==r) return EOF; return *l++;
}
F int _read(){
int x=0,f=1; char ch=readc();
while (!isdigit(ch)) { if (ch=='-') f=-1; ch=readc(); }
while (isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=readc();
return x*f;
}
F P operator - (P a,P b){ return (P){a.x-b.x,a.y-b.y}; }
F DB operator * (P a,P b){ return a.x*b.y-a.y*b.x; }
F bool operator < (L x,L y){
return x.ag!=y.ag?x.ag<y.ag:(x.b-x.a)*(y.b-x.a)>0;
}
F P IS (L x,L y){//交点
DB p=(y.b-x.a)*(x.b-x.a),q=(x.b-x.a)*(y.a-x.a),z=p/(p+q);
return (P){y.b.x+(y.a.x-y.b.x)*z,y.b.y+(y.a.y-y.b.y)*z};
}
F bool pd(L x,L y,L z){ return (z.b-z.a)*(IS(x,y)-z.a)<0; }
F void Hpi(){//半平面交
sort(t+1,t+k+1); int l=1,r=nd=0;
for (int i=1;i<=k;i++)
nd+=t[i].ag!=t[i-1].ag,t[nd]=t[i];
k=nd,nd=0,q[++r]=t[1],q[++r]=t[2];
for (int i=3;i<=k;i++){
while (l<r&&pd(q[r-1],q[r],t[i])) r--;
while (l<r&&pd(q[l+1],q[l],t[i])) l++;
q[++r]=t[i];
}
while (l<r&&pd(q[r-1],q[r],q[l])) r--; q[r+1]=q[l];
for (int i=l;i<=r;i++) a[++nd]=IS(q[i],q[i+1]);
}
F void calc(){//计算面积
if (nd<3) return; a[++nd]=a[1];
for (int i=1;i<=nd;i++) ans+=a[i]*a[i+1];
ans=abs(ans)/2;
}
int main(){
n=_read();
for (int i=1;i<=n;i++){
int m=_read();
for (int j=1;j<=m;j++)
tmp[j].x=_read(),tmp[j].y=_read();
tmp[m+1]=tmp[1];
for (int j=1;j<=m;j++)
t[++k].a=tmp[j],t[k].b=tmp[j+1];
}
for (int i=1;i<=k;i++)
t[i].ag=atan2(t[i].b.y-t[i].a.y,t[i].b.x-t[i].a.x);
return Hpi(),calc(),printf("%.3f",ans),0;
}