计算几何--(半平面判断多边形是否存在内核以及内核面积计算)

转载:http://blog.csdn.net/accry/article/details/6070621

首先解决问题:什么是半平面? 顾名思义,半平面就是指平面的一半,我们知道,一条直线可以将平面分为两个部分,那么这两个部分就叫做两个半平面。

然后,半平面怎么表示呢? 二维坐标系下,直线可以表示为ax + by + c = 0,那么两个半平面则可以表示为ax + by + c >= 0 和ax + by + c < 0,这就是半平面的表示方法。

还有,半平面的交是神马玩意? 其实就是一个方程组,让你画出满足若干个式子的坐标系上的区域(类似于线性规划的可行域),方程组就是由类似于上面的这些不等式组成的。

另外,半平面交可以干什么? 半平面交虽然说是半平面的问题,但它其实就是关于直线的问题。一个一个的半平面其实就是一个一个有方向的直线而已。

半平面交的一个重要应用就是求多边形的核 。 多边形的核又是神马玩意?  它是平面简单多边形的核是该多边形内部的一个点集,该点集中任意一点与多边形边界上一点的连线都处于这个多边形内部。就是一个在一个房子里面放一个摄像 头,能将所有的地方监视到的放摄像头的地点的集合即为多边形的核。经常会遇到让你判定一个多边形是否有核的问题。

比如说:

POJ 3335 Rotating Scoreboard
http://acm.pku.edu.cn/JudgeOnline/problem?id=3335

代码:http://blog.csdn.net/u013050857/article/details/48848041

POJ 1474 Video Surveillance
http://acm.pku.edu.cn/JudgeOnline/problem?id=1474

代码:http://blog.csdn.net/u013050857/article/details/48859751

POJ 1279 Art Gallery
http://acm.pku.edu.cn/JudgeOnline/problem?id=1279

代码:http://blog.csdn.net/u013050857/article/details/48859661

这三个题比较简单,裸的半平面交。

半平面交的最终奥义就是求出一个满足条件的凸多边形 ,而解决这个问题的前提就是直线切割多边形 ,让直线不断的去切割当前多边形,然后得到新的多边形,然后继续。。。最终得到一个符合条件的多边形,如果最终的多边形顶点数目为0,那么就说明半平面交无解(半平面交的解可以是一个凸多边形,一条直线,一个点,我们用顶点来记录这些解)。关于直线切割多边形,流程 是这样的:
对 凸多边形(指的是当前多边形)的每一个顶点,如果这个顶点在直线的指定的一侧(暨在该半平面上),那么就将该顶点直接加入到新的多边形中,否则,看与该点 相邻的多边形上的两个点(判断线段是否和直线相交),如果和直线相交,则把交点加入到新的多边形中。这样,就可以得到一个新的凸多边形。关于最初的多边 形,我们可以设一个四方形的区域,比如说(-1000,-1000),(-1000,1000),(1000,1000),(1000,-1000),然 后开始切割。

O(nlogn)复杂度的写法:参考《IOI国家集训队论文》:

【分析】:

算法大体思路是这样的:
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底部的半平面
重复a,b直到不能删除为止。
step5:计算出deque顶端和底部的交点即可。

需要好好理解~~

/*
* Complier: G++
* Author: herongwei
* Create Time: 15:34 2015/10/1 星期四
*/
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;

const double pi=acos(-1.0);
const double e=exp(1.0);
const double eps=1e-8;
const int maxn=55;
int dq[maxn],top,bot,order[maxn],cnt;
struct Point          // 点或向量结构
{
    double x,y;
    Point(double _x=0.0,double _y=0.0):x(_x),y(_y) {}
    Point operator - (const Point &p)
    {
        return Point(x-p.x,y-p.y);
    }
    double sqrx()    //向量的模
    {
        return sqrt(x*x+y*y);
    }
} point[maxn];//记录最开始的多边形

struct Line
{
    Point a,b;
    double angle;
}lne[maxn];

Point temp[maxn];//临时保存新切割的多边形
Point p[maxn];//保存新切割出的多边形
int pre_point,last_point; //原先的点数,新切割出的多边形的点数
double a,b,c;
/*
已知两点(x1,y1),(x2,y2)
则直线坐标为:(y2-y1)*x+(x1-x2)*y+(y1*x2-x1*y2)==0
*/
int dcmp(double x)
{
    if(fabs(x)<eps) return 0;
    return x>0?1:-1;
}

void getline(Point x,Point y)//获取直线ax+by+c==0
{
    a=y.y-x.y;
    b=x.x-y.x;
    c=y.x*x.y-x.x*y.y;
}

double multi(Point p0, Point p1, Point p2)
{
    return (p1.x-p0.x)*(p2.y-p0.y) - (p1.y-p0.y)*(p2.x-p0.x);
}

bool cmp(int u, int v)
{
    int d = dcmp(lne[u].angle-lne[v].angle);
    if(!d) return dcmp(multi(lne[u].a, lne[v].a, lne[v].b)) > 0;//大于0取向量左半部分为半平面,小于0,取右半部分
    return d < 0;
}

//获取直线ax+by+c==0  和点x和y所连直线的交点
void getIntersect(Line l1, Line l2, Point& p)
{
    double dot1, dot2;
    dot1 = multi(l2.a, l1.b, l1.a);
    dot2 = multi(l1.b, l2.b, l1.a);
    p.x = (l2.a.x * dot2 + l2.b.x * dot1) / (dot2 + dot1);
    p.y = (l2.a.y * dot2 + l2.b.y * dot1) / (dot2 + dot1);
}

bool judge(Line l0, Line l1, Line l2)
{
    Point p;
    getIntersect(l1, l2, p);
    return dcmp(multi(p, l0.a, l0.b)) < 0;//大于小于符号与上面cmp()中注释处相反
}

void addLine(double x1, double y1, double x2, double y2)
{
    lne[cnt].a.x = x1;
    lne[cnt].a.y = y1;
    lne[cnt].b.x = x2;
    lne[cnt].b.y = y2;
    lne[cnt].angle =atan2(y2-y1, x2-x1);
    order[cnt] = cnt;
    cnt++;
}

void halfPlaneIntersection()
{
    int i,j;
    sort(order, order+cnt, cmp);
    for(i = 1, j = 0; i < cnt; i++)
        if(dcmp(lne[order[i]].angle-lne[order[j]].angle) > 0)
            order[++j] = order[i];
    cnt = j + 1;
    dq[0] = order[0];
    dq[1] = order[1];
    bot = 0;
    top = 1;
    for(i = 2; i <cnt; i++)
    {
        while(bot < top && judge(lne[order[i]], lne[dq[top-1]], lne[dq[top]]))
            top--;
        while(bot < top && judge(lne[order[i]], lne[dq[bot+1]], lne[dq[bot]]))
            bot++;
        dq[++top] = order[i];
    }
    while(bot < top && judge(lne[dq[bot]], lne[dq[top-1]], lne[dq[top]])) top--;
    while(bot < top && judge(lne[dq[top]], lne[dq[bot+1]], lne[dq[bot]])) bot++;
}

bool isThereACore()
{
    if(top-bot > 1) return true;
    return false;
}

int main()
{
     while(scanf("%d",&pre_point),pre_point){
         for(int i=0; i<pre_point; ++i){
             scanf("%lf%lf",&point[i].x,&point[i].y);
         }int i;
         for(cnt=i=0; i<pre_point-1; ++i)
            addLine(point[i].x,point[i].y,point[i+1].x,point[i+1].y);
         addLine(point[i].x,point[i].y,point[0].x,point[0].y);
         halfPlaneIntersection();
         if(isThereACore()) puts("1");
         else puts("0");
     }
     return 0;
}
/*
 6
66 13
96 61
76 98
13 94
4 0
45 68
8
27 21
55 14
93 12
56 95
15 48
38 46
51 65
64 31
0

1
0
*/

一般的O(n^2)的写法:

#include <math.h>
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;

const double pi=acos(-1.0);
const double e=exp(1.0);
const double eps=1e-8;
const int maxn=105;

struct Point{
  double x,y;
}point[maxn];

Point temp[maxn];
Point p[maxn];
int pre_point ,last_point;
double a,b,c;

void getline(Point x,Point y)//获取直线ax+by+c==0
{
    a=y.y-x.y;
    b=x.x-y.x;
    c=y.x*x.y-x.x*y.y;
}

Point intersect(Point x,Point y)//获取直线ax+by+c==0  和点x和y所连直线的交点
{
    double u=fabs(a*x.x+b*x.y+c);
    double v=fabs(a*y.x+b*y.y+c);
    Point ans;
    ans.x=(x.x*v+y.x*u)/(u+v);
    ans.y=(x.y*v+y.y*u)/(u+v);
    return ans;
}

void cut()//用直线ax+by+c==0切割多边形
{
    int cut_num=0;
    for(int i=1; i<=last_point; ++i)
    {
        if(a*p[i].x+b*p[i].y+c>=0){
            temp[++cut_num]=p[i];
        }
        else
        {
            if(a*p[i-1].x+b*p[i-1].y+c>0)
            {
                temp[++cut_num]=intersect(p[i-1],p[i]);
            }
            if(a*p[i+1].x+b*p[i+1].y+c>0)
            {
                temp[++cut_num]=intersect(p[i+1],p[i]);
            }
        }
    }
    for(int i=1; i<=cut_num; ++i)
    {
        p[i]=temp[i];
    }
    p[cut_num+1]=temp[1];
    p[0]=temp[cut_num];
    last_point=cut_num;
}

void solve()
{
    for(int i=1; i<=pre_point; ++i){
        p[i]=point[i];
    }
    point[pre_point+1]=point[1];
    p[pre_point+1]=p[1];
    p[0]=p[pre_point];
    last_point=pre_point;
    for(int i=1; i<=pre_point; ++i)
    {
        getline(point[i],point[i+1]);//根据point[i]和point[i+1]确定直线ax+by+c==0
        cut();//用直线ax+by+c==0切割多边形
    }
}
int main()
{
     int tot=1;
     while(cin>>pre_point&&pre_point){
         for(int i=1; i<=pre_point; ++i){
             cin>>point[i].x>>point[i].y;
         }
         solve();
         if(last_point==0) puts("0");
         else puts("1");
     }
     return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值