半平面交

Posted HocRiser

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了半平面交相关的知识,希望对你有一定的参考价值。

半平面交可以解决一些线性规划问题和纯几何问题,比较常用于求多边形核等。
半平面交有两种写法,一种是比较直观的$O(n^2)$写法,一种是$O(n \log n)$写法。
$O(n^2)$的写法的思路是:设当前已经得到了一个半平面交的有序点集p(若半平面交封闭则必然是凸包),对于当前正在处理的直线,$O(n)$查找它能否通过切割将凸包的面积缩小,同时算出它与凸包边界的交点,从而更新凸包。
$O(n \log n)$的思想于此类似,但是使用双端队列优化,主要思路是:

step1. 将所有半平面按极角排序,对于极角相同的,选择性的保留一个。 O(nlogn)
step2. 使用一个双端队列(deque),加入最开始2个半平面。
step3. 每次考虑一个新的半平面:
  a.while deque顶端的两个半平面的交点在当前半平面外:删除deque顶端的半平面
  b.while deque底部的两个半平面的交点在当前半平面外:删除deque底部的半平面
  c.将新半平面加入deque顶端
step4.删除两端多余的半平面,具体方法是:
  a.while deque顶端的两个半平面的交点在底部半平面外:删除deque顶端的半平面
  b.while deque底部的两个半平面的交点在顶端半平面外:删除deque底部的半平面
step5:计算出deque顶端和底部的交点即可。

首先第一步将所有直线按照斜率排序,同时每条直线都确定一个方向使得半平面一定在它的“左侧”,这样对于斜率相同的直线一定只有最“靠左“的直线会起作用。
然后先加入前两个半平面,接着依次加入半平面。最后删除多余的不符合条件的半平面。最后要记得计算顶端和底部的交点使凸包封闭。

对于两点式直线求交的问题,可以直接列式解方程。
$y_1=k_1x+b_1$,$k_1=\frac{l_1.y_2-l_1.y_1}{l_1.x_2-l_1.x_1}$($l_1.y_2$表示第一条直线的第二个点的纵坐标), $y_2$同理
将上式代入$k_1x+b_1=k_2x+b_2$化简即可。

用两点式时对于平行与x轴或y轴的直线也一样处理,解直线不等式时,对于直线$ax+by+c \leqslant 0$,若$a=0$则加入$(0,-c/b)$和$(1,-c/b)$,若$b=0$则加入$(-c/a,0)$和$(-c/a,1)$,若均不为0则加入$(0,-c/b)$和$(1,-a/b-c/b)$。

$O(n^2)$的写法:

POJ3335求多边形核:

 1 #include<cstdio>
 2 #include<cmath>
 3 #include<algorithm>
 4 #define rep(i,l,r) for (i=l; i<=r; i++)
 5 using namespace std;
 6 
 7 const int N=210,inf=0x3f3f3f3f;
 8 const double eps=1e-8;
 9 inline double sgn(double x){ return (fabs(x)<eps) ? 0 : ( x>0 ? 1 : -1); }
10 
11 int i,T,ntot,n,tot;
12 struct P{ double x,y; }p[N],a[N],s[N];
13 struct S{ P s,t; };
14 
15 double cross(P a,P b,P c) { return (b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x); }
16 bool outside(S seg,P p){ return cross(seg.s,seg.t,p)>eps; }
17 bool inside(S seg,P p){ return cross(seg.s,seg.t,p)<-eps; }
18 
19 void work(P p1,P p2,P p3,P p4){
20     double t1=cross(p1,p2,p3),t2=cross(p1,p2,p4);
21     if (sgn(t1)*sgn(t2)<0){
22         double x=(fabs(t2)*p3.x+fabs(t1)*p4.x)/(fabs(t1)+fabs(t2));
23         double y=(fabs(t2)*p3.y+fabs(t1)*p4.y)/(fabs(t1)+fabs(t2));
24         a[++tot]=(P){x,y};
25     }
26 }
27 
28 void cut(S seg){
29     int i; tot=0;
30     rep(i,1,ntot){
31         if (!outside(seg,p[i])) a[++tot]=p[i];
32         else work(seg.s,seg.t,p[i-1],p[i]),work(seg.s,seg.t,p[i],p[i+1]);
33     }
34     ntot=tot; swap(a,p); p[0]=p[tot]; p[tot+1]=p[1];
35 }
36 
37 void solve(){
38     rep(i,1,n) scanf("%lf%lf",&s[i].x,&s[i].y); s[n+1]=s[1];
39     p[1]=(P){-inf,-inf}; p[2]=(P){-inf,inf}; p[3]=(P){inf,inf}; p[4]=(P){inf,-inf};
40     ntot=4; p[0]=p[4]; p[5]=p[1];
41     rep(i,1,n) cut((S){s[i],s[i+1]});
42     if (tot==0) puts("NO"); else puts("YES");
43 }
44 
45 int main(){
46     for (scanf("%d",&T); T--; ) scanf("%d",&n),solve();
47     return 0;
48 }

POJ2540(求直线不等式的解集)

 1 #include<cstdio>
 2 #include<cmath>
 3 #include<algorithm>
 4 #define rep(i,l,r) for (i=l; i<=r; i++)
 5 using namespace std;
 6 
 7 const double eps=1e-8;
 8 char str[10];
 9 int np,nq;
10 struct P { double x,y; }pre,cur,p[110],q[110];
11 double det(P a,P b,P c){ return (b.x-a.x)*(c.y-a.y)-(c.x-a.x)*(b.y-a.y); }
12 
13 void get(P p1,P p2,double &a,double &b,double &c){
14     if (fabs(p1.x-p2.x)<eps) a=0,b=1;
15     else if (fabs(p1.y-p2.y)<eps) a=1,b=0;
16         else b=p2.y-p1.y,a=p2.x-p1.x;
17     c=-a*(p2.x+p1.x)/2-b*(p2.y+p1.y)/2;
18 }
19 
20 void work(P p1,P p2,double a,double b,double c){
21     double u=a*p1.x+b*p1.y+c,v=a*p2.x+b*p2.y+c;
22     if (u*v<-eps){
23         double x=(fabs(u)*p2.x+fabs(v)*p1.x)/(fabs(u)+fabs(v));
24         double y=(fabs(u)*p2.y+fabs(v)*p1.y)/(fabs(u)+fabs(v));
25         q[++nq]=(P){x,y};
26     }
27 }
28 
29 void cut(double a,double b,double c){
30     int i; nq=0;
31     rep(i,1,np){
32         if (a*p[i].x+b*p[i].y+c>-eps) q[++nq]=p[i];
33         else work(p[i-1],p[i],a,b,c),work(p[i],p[i+1],a,b,c);
34     }
35     swap(q,p); swap(nq,np); p[np+1]=p[1]; p[0]=p[np];
36 }
37 
38 double area(int n){
39     int i; double ar=0;
40     rep(i,2,n-1) ar+=det(p[1],p[i],p[i+1]);
41     return fabs(ar)/2.0;
42 }
43 
44 int main(){
45     p[1]=(P){0,0}; p[2]=(P){0,10}; p[3]=(P){10,10}; p[4]=(P){10,0};
46     np=4; p[0]=p[4]; p[5]=p[1];
47     double ar=1.0;
48     while (scanf("%lf%lf%s",&cur.x,&cur.y,str)!=EOF){
49         double a,b,c; get(pre,cur,a,b,c);
50         if (str[0]==C)
51             { if (a*pre.x+b*pre.y+c<-eps) a=-a,b=-b,c=-c; }
52         else
53             if (str[0]==H)
54                 { if (a*cur.x+b*cur.y+c<-eps) a=-a,b=-b,c=-c;}
55             else ar=0;
56         if (fabs(ar)<eps) { puts("0.00"); continue; }
57         cut(a,b,c); ar=area(np);
58         printf("%.2f\n",ar); pre=cur;
59     }
60     return 0;
61 }

$O(n \log n)$写法:

BZOJ2618:

 1 #include<cmath>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #define rep(i,l,r) for (int i=l; i<=r; i++)
 5 using namespace std;
 6 
 7 const int N=1010;
 8 const double eps=1e-8;
 9 struct P{
10     double x,y;
11     P(double xx=0,double yy=0):x(xx),y(yy){}
12 }p[N],a[N];
13 struct L{P a,b; double slop;}l[1005],q[1005];
14 
15 int n,k,cnt,tot;
16 double ans;
17 
18 P operator -(P a,P b){ return P(a.x-b.x,a.y-b.y); }
19 double operator *(P a,P b){ return a.x*b.y-a.y*b.x; }
20 bool operator <(L a,L b){ return (a.slop!=b.slop) ? (a.slop-b.slop)<-eps : (a.b-a.a)*(b.b-a.a)>eps; }
21 
22 P inter(L a,L b){
23     double A1=a.b.y-a.a.y,B1=a.a.x-a.b.x,C1=(a.b.x-a.a.x)*a.a.y-(a.b.y-a.a.y)*a.a.x;
24     double A2=b.b.y-b.a.y,B2=b.a.x-b.b.x,C2=(b.b.x-b.a.x)*b.a.y-(b.b.y-b.a.y)*b.a.x;
25     return P((C2*B1-C1*B2)/(A1*B2-A2*B1),(C1*A2-C2*A1)/(A1*B2-A2*B1));
26 }
27 
28 bool jud(L a,L b,L t){ P p=inter(a,b); return (t.b-t.a)*(p-t.a)<0; }
29 
30 void work(){
31     sort(l+1,l+cnt+1);
32     int L=1,R=0; tot=0;
33     rep(i,1,cnt){
34         if (l[i].slop!=l[i-1].slop) tot++;
35         l[tot]=l[i];
36     }
37     cnt=tot; tot=0;
38     q[++R]=l[1]; q[++R]=l[2];
39     rep(i,3,cnt){
40         while (L<R && jud(q[R-1],q[R],l[i])) R--;
41         while (L<R && jud(q[L+1],q[L],l[i])) L++;
42         q[++R]=l[i];
43     }
44     while (L<R && jud(q[R-1],q[R],q[L])) R--;
45     while (L<R && jud(q[L+1],q[L],q[R])) L++;
46     q[R+1]=q[L];
47     rep(i,L,R) a[++tot]=inter(q[i],q[i+1]);
48 }
49 
50 void getans(){
51     if (tot<3) return;
52     a[++tot]=a[1];
53     rep(i,1,tot) ans+=a[i]*a[i+1];
54     ans=fabs(ans)/2;
55 }
56 
57 int main(){
58     scanf("%d",&n);
59     rep(i,1,n){
60         scanf("%d",&k);
61         rep(j,1,k) scanf("%lf%lf",&p[j].x,&p[j].y);
62         p[k+1]=p[1];
63         rep(j,1,k) l[++cnt].a=p[j],l[cnt].b=p[j+1];
64     }
65     rep(i,1,cnt) l[i].slop=atan2(l[i].b.y-l[i].a.y,l[i].b.x-l[i].a.x);
66     work(); getans(); printf("%.3lf\n",ans);
67     return 0;
68 }

BZOJ4445:

 1 #include<cmath>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #define rep(i,l,r) for (int i=l; i<=r; i++)
 5 using namespace std;
 6 
 7 const int N=100010;
 8 const double eps=1e-8;
 9 struct P{
10     double x,y;
11     P(double xx=0,double yy=0):x(xx),y(yy){}
12 }p[N],a[N];
13 struct L{P a,b; double slop;}l[N],q[N];
14 
15 int n,k,cnt,tot;
16 double ans1,ans2;
17 
18 P operator -(P a,P b){ return P(a.x-b.x,a.y-b.y); }
19 double operator *(P a,P b){ return a.x*b.y-a.y*b.x; }
20 bool operator <(L a,L b){ return (a.slop!=b.slop) ? (a.slop-b.slop)<-eps : (a.b-a.a)*(b.b-a.a)>eps; }
21 
22 P inter(L a,L b){
23     double A1=a.b.y-a.a.y,B1=a.a.x-a.b.x,C1=(a.b.x-a.a.x)*a.a.y-(a.b.y-a.a.y)*a.a.x;
24     double A2=b.b.y-b.a.y,B2=b.a.x-b.b.x,C2=(b.b.x-b.a.x)*b.a.y-(b.b.y-b.a.y)*b.a.x;
25     return P((C2*B1-C1*B2)/(A1*B2-A2*B1),(C1*A2-C2*A1)/(A1*B2-A2*B1));
26 }
27 
28 bool jud(L a,L b,L t){ P p=inter(a,b); return (t.b-t.a)*(p-t.a)<-eps; }
29 void access(int x){ for (int y=0; x; y=x,x=f[x]) splay(x),ch[x][1]=y,upd(x); }
30 void work(){
31     sort(l+1,l+cnt+1);
32     int L=1,R=0; tot=0;
33     rep(i,1,cnt){
34         if (abs(l[i].slop-l[i-1].slop)>eps) tot++;
35         l[tot]=l[i];
36     }
37     cnt=tot; tot=0;
38     q[++R]=l[1]; q[++R]=l[2];
39     rep(i,3,cnt){
40         while (L<R && jud(q[R-1],q[R],l[i])) R--;
41         while (L<R && jud(q[L+1],q[L],l[i])) L++;
42         q[++R]=l[i];
43     }
44     while (L<R && jud(q[R-1],q[R],q[L])) R--;
45     while (L<R && jud(q[L+1],q[L],q[R])) L++;
46     q[R+1]=q[L];
47     rep(i,L,R) a[++tot]=inter(q[i],q[i+1]);
48 }
49 
50 void get1(){
51     ans1=0;
52     if (tot<3) return;
53     a[++tot]=a[1];
54     rep(i,1,tot) ans1+=a[i]*a[i+1];
55     ans1=fabs(ans1)/2;
56 }
57 
58 void get2(){
59     ans2=0; p[n+1]=p[1];
60     rep(i,1,n+1) ans2+=p[i]*p[i+1];
61     ans2=fabs(ans2)/2;
62 }
63 
64 void ins(int i,int j){
65     double a=p[1].y-p[i].y-p[2].y+p[j].y;
66     double b=p[2].x-p[j].x-p[1].x+p[i].x;
67     double c=p[1].x*p[2].y-p[2].x*p[1].y-p[i].x*p[j].y+p[j].x*p[i].y;
68     if (abs(a)<eps && abs(b)<eps) return;
69     if (abs(a)<eps){
70         if (b>0) l[++cnt].a=P(1,-c/b),l[cnt].b=P(0,-c/b);
71             else l[++cnt].a=P(0,-c/b),l[cnt].b=P(1,-c/b);
72     }
73     if (abs(b)<eps){
74         if (a>0) l[++cnt].a=P(-c/a,0),l[cnt].b=P(-c/a,1);
75             else l[++cnt].a=P(-c/a,1),l[cnt].b=P(-c/a,0);
76     }
77     if (abs(a)>eps && abs(b)>eps){
78         if (b>0) l[++cnt].a=P(1,-a/b-c/b),l[cnt].b=P(0,-c/b);
79             else l[++cnt].a=P(0,-c/b),l[cnt].b=P(1,-a/b-c/b);
80     }
81 }
82 
83 int main(){
84     freopen("bzoj4445.in","r",stdin);
85     freopen("bzoj4445.out","w",stdout);
86     scanf("%d",&n);
87     rep(i,1,n) scanf("%lf%lf",&p[i].x,&p[i].y);
88     rep(i,2,n) ins(i,i%n+1); l[++cnt].a=p[1],l[cnt].b=p[2];
89     rep(i,1,cnt) l[i].slop=atan2(l[i].b.y-l[i].a.y,l[i].b.x-l[i].a.x);
90     work(); get1(); get2(); printf("%.4lf\n",ans1/ans2);
91     return 0;
92 }

 

以上是关于半平面交的主要内容,如果未能解决你的问题,请参考以下文章

半平面交

半平面交

半平面交

BZOJ 1038 ZJOI2008 瞭望塔 半平面交

poj 3384 半平面交

[日常摸鱼]bzoj1038[ZJOI2008]瞭望塔-半平面交