半平面交算法及简单应用

半平面:一条直线把二维平面分成两个平面。

半平面交:在二维几何平面上,给出若干个半平面,求它们的公共部分

半平面交的结果:1.凸多边形(后面会讲解到)2.无界,因为有可能若干半平面没有形成封闭3.直线,线段,点,空(属于特殊情况吧)

算法:1:根据上图可以知道,运用给出的多边形每相邻两点形成一条直线来切割原有多边形,如果多边形上的点i在有向直线的左边或者在直线上即保存起来,否则判断此点的前一个点i-1和后一个点i+1是否在此直线的左边或线上,在的话分别用点i和点i-1构成的直线与此时正在切割的直线相交求出交点,这个交点显然也要算在切割后剩下的多边形里,同理点i和点i+1。原多边形有n条边,每条边都要进行切割,所以时间复杂度为O(n^2)。

2:第二种就是训练指南上面详细讲解的运用双端队列的半平面交算法,时间复杂度为O(nlogn)。仔细阅读代码应该能理解。

代码实现:以poj3130为例,裸的模板。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cstdlib>
 5 #include<cmath>
 6 #include<algorithm>
 7 #define inf 0x7fffffff
 8 #define exp 1e-10
 9 #define PI 3.141592654
10 using namespace std;
11 const int maxn=111;
12 struct Point
13 {
14     double x,y;
15     Point (double x=0,double y=0):x(x),y(y){}
16 }an[maxn],bn[maxn],cn[maxn];
17 ///an:记录最开始的多边形;bn:临时保存新切割的多边形;cn:保存新切割出的多边形
18 typedef Point Vector;
19 Vector operator + (Vector A,Vector B) {return Vector(A.x+B.x , A.y+B.y); }
20 Vector operator - (Vector A,Vector B) {return Vector(A.x-B.x , A.y-B.y); }
21 Vector operator * (Vector A,double p) {return Vector(A.x*p , A.y*p); }
22 Vector operator / (Vector A,double p) {return Vector(A.x/p , A.y/p); }
23 int dcmp(double x) {if (fabs(x)<exp) return 0;return x>0 ? 1 : -1;  }
24 double cross(Vector A,Vector B)
25 {
26     return A.x*B.y-B.x*A.y;
27 }
28
29 double A,B,C;
30 int n,m;
31 void getline(Point a,Point b)///获取直线 Ax + By + C = 0
32 {
33     A=b.y-a.y;
34     B=a.x-b.x;
35     C=b.x*a.y-a.x*b.y;
36 }
37 ///getline()函数得到的直线和点a和点b所连直线的交点
38 Point intersect(Point a,Point b)
39 {
40     double u=fabs(A*a.x+B*a.y+C);
41     double v=fabs(A*b.x+B*b.y+C);
42     Point ans;
43     ans.x=(a.x*v+b.x*u)/(u+v);
44     ans.y=(a.y*v+b.y*u)/(u+v);
45     return ans;
46 }
47 void cut()///切割,原多边形的点为顺时针存储
48 {
49     int cnt=0;
50     for (int i=1 ;i<=m ;i++)
51     {
52         if (A*cn[i].x + B*cn[i].y + C>=0) bn[++cnt]=cn[i];
53         else
54         {
55             if (A*cn[i-1].x + B*cn[i-1].y + C > 0) bn[++cnt]=intersect(cn[i-1],cn[i]);
56             if (A*cn[i+1].x + B*cn[i+1].y + C > 0) bn[++cnt]=intersect(cn[i+1],cn[i]);
57         }
58     }
59     for (int i=1 ;i<=cnt ;i++) cn[i]=bn[i];
60     cn[0]=bn[cnt];
61     cn[cnt+1]=bn[1];
62     m=cnt;///新切割出的多边形的点数
63 }
64 void solve()
65 {
66     for (int i=1 ;i<=n ;i++) cn[i]=an[i];
67     an[n+1]=an[1];
68     cn[n+1]=an[1];
69     cn[0]=an[n];
70     m=n;
71     for (int i=1 ;i<=n ;i++)
72     {
73         getline(an[i],an[i+1]);
74         cut();
75     }
76 }
77 int main()
78 {
79     while (scanf("%d",&n)!=EOF && n)
80     {
81         for (int i=1 ;i<=n ;i++) scanf("%lf%lf",&an[i].x,&an[i].y);
82         reverse(an+1,an+n+1);
83         solve();
84         if (m) puts("1");
85         else puts("0");
86     }
87     return 0;
88 }

poj3335,给出两种方法

1.O(n^2)

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cstdlib>
 5 #include<cmath>
 6 #include<algorithm>
 7 #define inf 0x7fffffff
 8 #define exp 1e-10
 9 #define PI 3.141592654
10 using namespace std;
11 const int maxn=111;
12 struct Point
13 {
14     double x,y;
15     Point (double x=0,double y=0):x(x),y(y){}
16 }an[maxn],bn[maxn],cn[maxn];
17 typedef Point Vector ;
18 Vector operator + (Vector A,Vector B) {return Vector(A.x+B.x , A.y+B.y); }
19 Vector operator - (Vector A,Vector B) {return Vector(A.x-B.x , A.y-B.y); }
20 Vector operator * (Vector A,double p) {return Vector(A.x*p , A.y*p); }
21 Vector operator / (Vector A,double p) {return Vector(A.x/p , A.y/p); }
22 int dcmp(double x) {if (fabs(x)<exp) return 0;return x>0 ? 1 : -1; }
23 double cross(Vector A,Vector B)
24 {
25     return A.x*B.y-B.x*A.y;
26 }
27
28 int n,m;
29 double A,B,C;
30 void getline(Point a,Point b)
31 {
32     A=b.y-a.y;
33     B=a.x-b.x;
34     C=b.x*a.y-a.x*b.y;
35 }
36 Point intersect(Point a,Point b)
37 {
38     double u=fabs(A*a.x+B*a.y+C);
39     double v=fabs(A*b.x+B*b.y+C);
40     Point ans;
41     ans.x=(a.x*v+b.x*u)/(u+v);
42     ans.y=(a.y*v+b.y*u)/(u+v);
43     return ans;
44 }
45 void cut()
46 {
47     int cnt=0;
48     for (int i=1 ;i<=m ;i++)
49     {
50         if (A*cn[i].x+B*cn[i].y+C>=0)
51             bn[++cnt]=cn[i];
52         else
53         {
54             if (A*cn[i-1].x+B*cn[i-1].y+C>0)
55                 bn[++cnt]=intersect(cn[i-1],cn[i]);
56             if (A*cn[i+1].x+B*cn[i+1].y+C>0)
57                 bn[++cnt]=intersect(cn[i+1],cn[i]);
58         }
59     }
60     for (int i=1 ;i<=cnt ;i++) cn[i]=bn[i];
61     cn[0]=bn[cnt];
62     cn[cnt+1]=bn[1];
63     m=cnt;
64 }
65 void solve()
66 {
67     for (int i=1 ;i<=n ;i++) cn[i]=an[i];
68     an[n+1]=an[1];
69     cn[n+1]=cn[1];
70     cn[0]=cn[n];
71     m=n;
72     for (int i=1 ;i<=n ;i++)
73     {
74         getline(an[i],an[i+1]);
75         cut();
76     }
77 }
78 int main()
79 {
80     int t;
81     scanf("%d",&t);
82     while (t--)
83     {
84         scanf("%d",&n);
85         for (int i=1 ;i<=n ;i++) scanf("%lf%lf",&an[i].x,&an[i].y);
86         solve();
87         if (!m) printf("NO\n");
88         else printf("YES\n");
89     }
90     return 0;
91 }

2.O(nlogn)

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<cstdlib>
  5 #include<cmath>
  6 #include<algorithm>
  7 #define inf 0x7fffffff
  8 #define exp 1e-10
  9 #define PI 3.141592654
 10 using namespace std;
 11 const int maxn=111;
 12 struct Point
 13 {
 14     double x,y;
 15     Point (double x=0,double y=0):x(x),y(y){}
 16 }an[maxn];
 17 typedef Point Vector;
 18 struct Line
 19 {
 20     Point p;
 21     Vector v;
 22     double ang;
 23     Line (){}
 24     Line (Point p,Vector v):p(p),v(v){ang=atan2(v.y,v.x); }
 25     //Line (Point p,Vector v):p(p),v(v) {ang=atan2(v.y,v.x); }
 26     friend bool operator < (Line a,Line b)
 27     {
 28         return a.ang<b.ang;
 29     }
 30 }bn[maxn];
 31 Vector operator + (Vector A,Vector B) {return Vector(A.x+B.x , A.y+B.y); }
 32 Vector operator - (Vector A,Vector B) {return Vector(A.x-B.x , A.y-B.y); }
 33 Vector operator * (Vector A,double p) {return Vector(A.x*p , A.y*p); }
 34 Vector operator / (Vector A,double p) {return Vector(A.x/p , A.y/p); }
 35 int dcmp(double x) {if (fabs(x)<exp) return 0;return x>0 ? 1 : -1; }
 36 double cross(Vector A,Vector B)
 37 {
 38     return A.x*B.y-B.x*A.y;
 39 }
 40 bool OnLeft(Line L,Point p)
 41 {
 42     return cross(L.v,p-L.p)>=0;///点P在有向直线L的左边(>=0说明在线上也算)
 43 }
 44 Point GetIntersection(Line a,Line b)
 45 {
 46     Vector u=a.p-b.p;
 47     double t=cross(b.v,u)/cross(a.v,b.v);
 48     return a.p+a.v*t;
 49 }
 50 //Point GetIntersection(Line l1, Line l2) {
 51 //    Point p;
 52 //    double dot1,dot2;
 53 //    //dot1 = multi(l2.a, l1.b, l1.a);
 54 //    dot1=cross(l1.b-l2.a , l1.a-l2.a);
 55 //    //dot2 = multi(l1.b, l2.b, l1.a);
 56 //    dot2=cross(l2.b-l1.b , l1.a-l1.b);
 57 //    p.x = (l2.a.x * dot2 + l2.b.x * dot1) / (dot2 + dot1);
 58 //    p.y = (l2.a.y * dot2 + l2.b.y * dot1) / (dot2 + dot1);
 59 //    return p;
 60 //}
 61 int HalfplaneIntersection(Line *L,int n,Point *poly)
 62 {
 63     sort(L,L+n);
 64
 65     int first,last;
 66     Point *p=new Point[n];
 67     Line *q=new Line[n];
 68     q[first=last=0]=L[0];
 69     for (int i=1 ;i<n ;i++)
 70     {
 71         while (first<last && !OnLeft(L[i],p[last-1])) last--;
 72         while (first<last && !OnLeft(L[i],p[first])) first++;
 73         q[++last]=L[i];
 74         if (fabs(cross(q[last].v , q[last-1].v))<exp)
 75         {
 76             last--;
 77             if (OnLeft(q[last] , L[i].p)) q[last]=L[i];
 78         }
 79         if (first<last) p[last-1]=GetIntersection(q[last-1],q[last]);
 80     }
 81     while (first<last && !OnLeft(q[first],p[last-1])) last--;
 82     if (last-first<=1) return 0;
 83     p[last]=GetIntersection(q[last],q[first]);
 84     int m=0;
 85     for (int i=first ;i<=last ;i++) poly[m++]=p[i];
 86     return m;
 87 }
 88 void calPolygon(Point *p, int n, double &area, bool &shun)
 89 {
 90     p[n] = p[0];
 91     area = 0;
 92     double tmp;
 93     for (int i = 0; i < n; i++)
 94         area += p[i].x * p[i + 1].y - p[i].y * p[i + 1].x;
 95     area /= 2.0;
 96     if (shun = area < 0)
 97         area = -area;
 98 }
 99 bool calCore(Point *ps, int n)
100 {
101     Line l[maxn];
102     ps[n] = ps[0];
103     bool shun;
104     double area;
105     calPolygon(ps, n, area, shun);
106     if (shun)
107         for (int i = 0; i < n; i++)
108             bn[i] = Line(ps[i], ps[i] - ps[i + 1]);
109     else
110         for (int i = 0; i < n; i++)
111             bn[i] = Line(ps[i], ps[i + 1] - ps[i]);
112     Point pp[maxn];
113     return HalfplaneIntersection(bn, n, pp);
114 }
115 int main()
116 {
117     int t;
118     int n;
119     scanf("%d",&t);
120     while (t--)
121     {
122         scanf("%d",&n);
123         Point cn[maxn];
124         for (int i=0 ;i<n ;i++)
125         {
126             scanf("%lf%lf",&cn[i].x,&cn[i].y);
127         }
128 //        reverse(cn,cn+n);
129 //        for (int i=0 ;i<n ;i++)
130 //        {
131 //            bn[i].p=cn[i];
132 //            bn[i].v=cn[(i+1)%n]-cn[i];
133 //            bn[i].ang=atan2(bn[i].v.y , bn[i].v.x);
134 //        }
135 //        int m=HalfplaneIntersection(bn,n,an);
136         if (!calCore(cn,n)) puts("NO");
137         else puts("YES");
138     }
139     return 0;
140 }

练习:

poj 1474

poj 2451

poj 3525

LA 2218

LA 2512

UVA 10084

后续:欢迎提出宝贵的意见。

时间: 2024-10-05 04:09:39

半平面交算法及简单应用的相关文章

半平面交模板 HDU 1469

题意就是要判断一个多边形是否存在核. 我们可以把沿着顺时针方向走这个多边形,对于每个边向量,我们取其右边的半平面,判断交是否为空即可. 对于半平面交算法,我只理解了O(n^2)的算法,大概就是用向量去切割多边形,对于O(nlogn)的算法,我从网上各种搜集以及参考了蓝书的实现,给出了一份能看的代码. O(n^2) 1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<cmat

【BZOJ2618】【Cqoi2006】凸多边形 半平面交 、算法的深度细节剖析。

题解:虽然这道题数据范围太小,O(n*n)的算法都能过,但是我为了练手仍写了半平面交.. 半平面交: 我们规定:一个基准点+一个向量(本质是有向直线,)就算一个半平面,现在要求出半平面交,然后做一些事情.. 那么可以过每个半平面的基准点做一条平行于x轴的向右的射线,此射线与向量代表直线的夹角为其"极角".. 我们可以把所有半平面(Line,或者说叫有向直线)以其极角为键值排序, 然后扫一圈,围出来一个图形,即要求的半平面交.. 实现过程不妨看代码,有详细注释. 代码中有几个需要画图的地

简单几何(半平面交+二分) LA 3890 Most Distant Point from the Sea

题目传送门 题意:凸多边形的小岛在海里,问岛上的点到海最远的距离. 分析:训练指南P279,二分答案,然后整个多边形往内部收缩,如果半平面交非空,那么这些点构成半平面,存在满足的点. /************************************************ * Author :Running_Time * Created Time :2015/11/10 星期二 14:16:17 * File Name :LA_3890.cpp ********************

半平面交 求多边形内核问题

多边形的内核可以理解为: 在多边形找到一块区域,使这块区域中的任何一个点都能够和多边形上的任意一点相连而不受到多边形上其他边的阻挡 也可以抽象的理解为在这块区域中任意位置放一个旋转摄像头,这个摄像头可以监控多边形的整个区域 多边形内核是否存在可以利用半平面交的思想去求解 将多边形上的每一条边作为逆时针顺序的射线,在每一条线左侧的就是可行区域 譬如: 比如这个多边形 蓝色区域便是内核 这样利用半平面交的nlogn的算法就可以轻松判断 这里是三道这样类型的题目:poj 3335 , poj 1474

POJ 3335 Rotating Scoreboard(半平面交求多边形核)

题目链接 题意 : 给你一个多边形,问你在多边形内部是否存在这样的点,使得这个点能够看到任何在多边形边界上的点. 思路 : 半平面交求多边形内核. 半平面交资料 关于求多边形内核的算法 什么是多边形的内核? 它是平面简单多边形的核是该多边形内部的一个点集,该点集中任意一点与多边形边界上一点的连线都处于这个多边形内部.就是一个在一个房子里面放一个摄像 头,能将所有的地方监视到的放摄像头的地点的集合即为多边形的核. 如上图,第一个图是有内核的,比如那个黑点,而第二个图就不存在内核了,无论点在哪里,总

POJ 2451 nlog(n)半平面交裸题。

前言       最近学习C#,不过好在当初考计算机二级学习过C++,刚上手没有对C#感到很恐惧.C#视频也看了几天 了,总感觉不总结一下心里没底,现在跟着我从头走进C#之旅吧.     C#是以后总面向对象的编程语言(OOP),C#是从C和C++派生出来的,主要用于开发可以运行在.NET平台 上的应用程序.随着.NET的发展,C#语言简单.现代.面向对象和类型安全显示了一定的优势.     下面我就介绍一些初学者不太理解的一些东西.   C#有以下突出的特点       (1)语法简洁.不允许

例4.10 POJ3525/LA3890离海最远的点 半平面交 + 二分法 + double小数点后有效位数处理方式/printf与g++、c++的问题

0) 题意: 题意很简单,给出一张四面环海的岛屿的地图,岛屿用顶点表示(题目数据保证岛屿是凸多边形--所谓凸多边形与凹多边形区别,凸多边形就是把一个多边形任意一边向两方无限延长成为一条直线,如果多边形的其他各边均在此直线的同旁,那么这个多边形就叫做凸多边形.)找出岛屿上距离大海距离最长的一个点.即求岛屿上距离岛屿各条边边中最短的距离是所有点中最长的那个点.即求岛屿中的内接圆的圆心点.输出这个点到岛屿的边的最短的距离.即该岛屿中那个内接圆的半径... 分析: 半平面交求内核点集是一个点的情况(用精

POJ2451 Uyuw&#39;s Concert (半平面交)

POJ2451  给定N个半平面 求他们的交的面积. N<=20000 首先参考 POJ1279 多边形的核 其实就是这里要求的半平面交 但是POJ1279数据较小 O(n^2)的算法 看起来是要TLE的 但是试着提交了一下 一遍就A了... 看来暴力的半平面切割法实际表现远远好于O(n^2) 如果数据再大一点呢? N=100000? 有一个办法可以优化到O(nlogn) step1. 将所有半平面按极角排序,对于极角相同的,选择性的保留一个. O(nlogn) step2. 使用一个双端队列(

半平面交总结and模板

博客原文地址:http://blog.csdn.net/xuechelingxiao/article/details/40859973 这两天刷了POJ上几道半平面交,对半平面交有了初步的体会,感觉半平面交还是个挺实用的知识点. 半平面交主要是看的ZZY的国家队论文,他提出的是一种O(n×log(n))的排序增量法. 附论文地址: 算法合集之<半平面交的新算法及其实用价值>. POJ 3335 Rotating Scoreboard 题目大意: World finals 要开始了,比赛场地是一