这是一道半平面交的裸题,第一次写半平面交,就说一说我对半平面交的理解吧。
所谓半平面交,就是求一大堆二元一次不等式的交集,而每个二元一次不等式的解集都可以看成是在一条直线的上方或下方,联系直线的标准方程就可以得出。于是乎这些不等式就可以转化为一些半平面,求的就是半平面交。
而半平面交不可能交出凹多边形(因为凹多边形的定义是有一条边所在的直线能把该多边形分成若干块。。。YY一下就知道这是不可能的),这是一个十分优美的性质,正类似于凸包(写法也是有些相似的),但半平面交可能交出无界,于是可以加四条类似于一个框的直线将这个区域框起来,就可以避免无界情况的出现,当然也可能交出无解,一个点或一条直线(俩点),判一判就好了。
有一个十分暴力的n^2做法,就是每次加一条直线,暴力去判与当前直线的交点,然后判断哪些部分不可行,哪些部分可行。。。(十分暴力,而且十分难写。。。。)
也有两个(可能是三个。。。不过后两个思想类似)nlogn的做法,一种是分治,将当前的半平面数分治,递归去找,然后利用扫描线扫过去。。。。不过常数大而且不易编写
还有另一个nlogn的做法,就是维护一个单调栈,用一个上凸壳和一个下凸壳处理,方法是如果直线stack[top]与当前直线a[i]交点在当前的半平面外就top--,然后把得到的一个上凸壳和一个下凸壳合并即可
但合并还是有些麻烦,就可以用一个双向队列维护一个凸壳,每次加入一条直线就判队头和队尾然后加在队尾,但具体实现过程中可能会出现问题,因为令极角递增(都是看成逆时针旋转,这样比较方便),所以当前直线的极角一定比队列中所有直线的极角都要大,因此对答案是一定有贡献的(也就是一定会是边界,在不被后面的直线判掉的情况下),在维护两个单调栈时并不用考虑(貌似合并时也要考虑),但deque是一个环,因此到最后的直线的斜率虽然大,但并不一定会对半平面产生贡献(毕竟每一次都是queue[++tail]=a[i]),因此就还需要两遍while判队头队尾什么的。这样十分方便而且核心代码巨短。。。。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 using namespace std; 7 #define maxn 2000 8 #define eps 1e-0 9 10 int n,m,tot,cnt; 11 12 struct point{ 13 double x,y; 14 }p[maxn],pt[maxn]; 15 16 struct line{ 17 point from,to; 18 double slope; 19 }l[maxn],q[maxn]; 20 21 point operator -(point a,point b){return(point){a.x-b.x,a.y-b.y};} 22 point operator +(point a,point b){return(point){a.x+b.x,a.y+b.y};} 23 double operator *(point a,point b){return a.x*b.y-a.y*b.x;} 24 bool operator <(line a,line b){ 25 return (a.slope==b.slope)?((a.to-a.from)*(b.to-a.from)<0):a.slope<b.slope; 26 } 27 bool operator ==(line a,line b){ 28 return a.slope==b.slope; 29 } 30 31 point getpoint(line a,line b){ 32 double t1=(b.to-a.from)*(a.to-a.from),t2=(a.to-a.from)*(b.from-a.from); 33 double t=t1/(t1+t2); 34 return (point){(b.from.x-b.to.x)*t+b.to.x,(b.from.y-b.to.y)*t+b.to.y}; 35 } 36 37 bool check(line a,line b,line c){ 38 point d=getpoint(a,b); 39 return (c.to-c.from)*(d-c.from)<0; 40 } 41 42 void solve(){ 43 int head=1,tail=2; 44 q[1]=l[1],q[2]=l[2]; 45 for (int i=3;i<=n;i++){ 46 while (head<tail && check(q[tail-1],q[tail],l[i])) tail--; 47 while (head<tail && check(q[head+1],q[head],l[i])) head++; 48 q[++tail]=l[i]; 49 } 50 while (head<tail && check(q[tail-1],q[tail],q[head])) tail--; 51 while (head<tail && check(q[head+1],q[head],q[tail])) head++; 52 q[tail+1]=q[head],cnt=0; 53 for (int i=head;i<=tail;i++) pt[++cnt]=getpoint(q[i],q[i+1]); 54 } 55 56 int main(){ 57 scanf("%d",&n); 58 for (int i=1;i<=n;i++){ 59 scanf("%d",&m); 60 for (int j=1;j<=m;j++) scanf("%lf %lf",&p[j].x,&p[j].y); 61 p[m+1]=p[1]; 62 for (int j=1;j<=m;j++) l[++tot].from=p[j],l[tot].to=p[j+1]; 63 } 64 for (int i=1;i<=tot;i++) l[i].slope=atan2(l[i].to.y-l[i].from.y,l[i].to.x-l[i].from.x); 65 sort(l+1,l+tot+1); 66 n=0; 67 n=unique(l+1,l+tot+1)-l; 68 n--; 69 solve(); 70 double ans=0; 71 if (cnt<=2){ 72 printf("0.000"); 73 return 0; 74 } 75 pt[++cnt]=pt[1]; 76 for (int i=1;i<=cnt;i++) ans+=pt[i]*pt[i+1]; 77 printf("%.3lf",ans/2); 78 return 0; 79 }