什么是半平面:半平面就是指平面的一半,我们知道,一条直线可以将平面分为两个部分,那么这两个部分就叫做两个半平面。
半平面怎么表示呢: 二维坐标系下,直线可以表示为ax + by + c = 0,那么两个半平面则可以表示为ax + by + c >= 0 和ax + by + c < 0,这就是半平面的表示方法。
半平面的交: 其实就是一个方程组,让你画出满足若干个式子的坐标系上的区域(类似于线性规划的可行域),方程组就是由类似于上面的这些不等式组成的。
半平面交有什么用:
1.求解一个区域,可以看到给定图形的各个角落。(多边形的核)
2.求可以放进多边形的圆的最大半径。
多边形的核: 它是平面简单多边形的核是该多边形内部的一个点集,该点集中任意一点与多边形边界上一点的连线都处于这个多边形内部。
求解半平面交的原理:
1我们用这个一个不规则图形举例子。
2选取一个正方向。(一般为逆时针),这样选取的好处是,保证核在有向线段的左边。
3把有向线段通过极角排序(与 x xx 轴的夹角)(-180°,180°](以x轴正向,上为正,下位负)。排序结果如下所示。
按照极角排序的原因是写代码方便,排序之后的线段是有序的,可以在双端队列里进行操作。
4按顺序遍历每条线段,取左边区域,删右边区域
开始时:
例如:加入第一条直线,保留左边区域,删除右边区域
5加入最后一条线段,保留左边区域,删除右边区域。
最后这时我们得到的是围成这个蓝色区域的直线集合。
如果至少有三条边,就说明该多边形有核(三条以上时,核为全部直线围成的凸包。)如果要求面积,我们可以将直线的交点求出来,然后再用叉积求凸包面积
解答因为我们对线段进行了排序,所以加入的线段会比前面的更“陡”。显然,如果先前的两条线段的交点在当前加入线段的右侧,则较“陡”的那条线段就会无效。
POJ3335【基础】
题目大意:
给一个有 n 个点(1<=n<=100)多边形,求这个多边形的核是否存在。所谓多边形核,指的是多边形内的一个点集(一片区域),这些点集中的任意一点与多边形边上任意一点的连线都不会与多边形的边相交。
输入:
第一行有一个整数 t,表示有 t 组测试数据。每一组测试数据一行,第一个整数 n,接下来有 n 对空格分隔的整数,表示一个点的 x y 坐标,点按顺时针顺序给出。
输出:
如果这是一个星形多边形,输出一行,包含一个数字 1,否则输出一行包含一
个数字 0。
#include <cstdio>
#include <cmath>
using namespace std;
#define eps 1e-8
#define MaxN 101
//二维点类
struct Point
{
double x,y;
Point(double a=0,double b=0){
x=a;y=b;}
};
typedef Point Vector;
//二维直线类,一般方程ax+by+c=0
struct Line
{
double a,b,c,angle;
Point p1,p2;
Line(Point s,Point e)
{
a=s.y-e.y;
b=e.x-s.x;
c=s.x*e.y-e.x*s.y;
angle=atan2(e.y-s.y,e.x-s.x);
p1=s;p2=e;
}
Line(){
}
};
Vector operator - (Point a,Point b)
{
return Vector(a.x-b.x,a.y-b.y);
}
//两向量求叉积,求三角形面积需要除以2
double Cross(Vector a,Vector b)
{
return a.x*b.y-b.x*a.y;
}
//采用eps的精度判断大/小于零
int epssgn(double x)
{
if (fabs(x)<eps) return 0;
else return x<0?-1:1;
}
//求两条直线l1和l2的交点
Point GetIntersect(Line l1, Line l2)
{
Point res;
res.x=(l1.b*l2.c-l2.b*l1.c)/(l1.a*l2.b-l2.a*l1.b);
res.y=(l1.c*l2.a-l2.c*l1.a)/(l1.a*l2.b-l2.a*l1.b);
return res;
}
/*-----半平面交模板-----*/
int cmp(const Line & l1,const Line & l2)
{
int d=epssgn(l1.angle-l2.angle);
if (!d) return (epssgn(Cross(l2.p1-l1.p1,l2.p2-l1.p1))>0); //极角相同时,将更靠半平面里面的放在前面
return d<0;
}
void QSort(Line L[],int l,int r)
{
int i=l,j=r;
Line swap,mid=L[(l+r)/2];
while (i<=j)
{
while (cmp(L[i],mid)) i++;
while (cmp(mid,L[j])) j--;
if (i<=j)
{
swap=L[i];
L[i]=L[j];
L[j]=swap;
i++;j--;
}
}
if (i<r) QSort(L,i,r);
if (l<j) QSort(L,l,j);
}
//判断l1与l2的交点是否在半平面hpl外
int IntersectionOutOfHalfPlane(Line &hpl,Line &l1,Line &l2)
{
Point p=GetIntersect(l1,l2);
return (epssgn(Cross(hpl.p1-p,hpl.p2-p))<0);
}
//求n个半平面l的半平面交,得到的交点储存在p中,交点数目返回到pn
void HalfPlaneIntersect(Line l[],int n,Point p[],int &pn)
{
int i,j;
int dq[MaxN],top,bot;
//排序是在满足所有半平面A*x+B*y+C>0或(<,<=,>=),
//也就是所有半平面的符号均相同的情况下对极角进行排序。
QSort(l,0,n-1);
//极角相同时,只保留最靠里面的那条
for (i=j=0;i<n;i++) if (epssgn(l[i].angle-l[j].angle)>0) l[++j]=l[i];
n=j+1;
dq[0]=0; //双端队列
dq[1]=1;
top=1; //顶部和底部
bot=0;
for (i=2;i<n;i++)
{
//当栈顶的两条直线交点在当前半平面外部时,弹栈
while (top>bot && IntersectionOutOfHalfPlane(l[i],l[dq[top]],l[dq[top-1]])) top--;
//由于求的是一个凸多边形,所以当半平面转过接近一圈时,某个半平面满足上一个while的条件后,
//它又会影响到底部的两条直线,当底部的两条直线的交点,在当前的半平面外部时,底部弹栈
while (top>bot && IntersectionOutOfHalfPlane(l[i],l[dq[bot]],l[dq[bot+1]])) bot++;
dq[++top]=i; //当前半平面入栈
}
//当最顶部的两条直线的交点不在最底部的半平面内时,顶部的那个半平面是多余的,顶部弹栈
while (top>bot && IntersectionOutOfHalfPlane(l[dq[bot]],l[dq[top]],l[dq[top-1]])) top--;
//当最底部的两条直线的交点不在最顶部的半平面内时,底部的那个半平面是多余的,底部弹栈
while (top>bot && IntersectionOutOfHalfPlane(l[dq[top]],l[dq[bot]],l[dq[bot+1]])) bot++;
dq[++top]=dq[bot]; //将最底部的半平面放到最顶部来,方便下面求顶点
for (pn=0,i=bot;i<top;i++,pn++) p[pn]=GetIntersect(l[dq[i+1]],l[dq[i]]);
}
/*-----半平面交模板-----*/
Line l[MaxN];
Point p[MaxN];
int n,pn;
int main()
{
int i,t;
double x1,y1,x2,y2;
freopen("poj3335.txt","r",stdin);
freopen("poj3335ans.txt","w",stdout);
scanf