半平面交初步

半平面交:

问题简述:

给定一些半平面,求他它们交集(大小,周长,\(\cdots\))。

半平面的表示:

半平面通常由一些关于直线的不等式给出,比如 \(y\le kx + b\) 半平面就是直线 \(y = kx + b\) 下方的一块区域。

而如果我们钦定半平面在有向直线的左侧,那么我们就可以通过一条有向直线来表示半平面。

struct VecLine//有向直线
{
    Point P;//直线上的某一点
    Vec v;//方向向量
    double ang;//直线的倾斜角

    Line() { }
    Line(Point P, Point W) : P(P) { v = W - P; ang = atan2(v.y, v.x); }
    //P, W为直线上两点,两点确定一条直线,方向为 P->W。

    bool operator < (const Line& B) const { return ang < B.ang; }

    bool Right(Point p) { return dcmp(v.Cross(p - P)) < 0; }//判断一个点是否在该直线的右侧(用叉积)。
} ; 

由于半平面是凸的,凸集的交集也一定是凸的,所以半平面交一定要么没有,是点,是线段,要么是一个凸多边形(或者一块无限区域,即无界多边形,但也是凸的)。

算法

增量法(在线)

复杂度 \(O(n^2)\) 的在线算法,每次增加一条直线就尝试去切割半平面交的多边形,具体实现咕咕咕。

极角排序(离线)

首先把所有直线极角排序,维护一个双端队列,队列里面存了当前半平面交的多边形上的直线以及多边形上的交点。

并且队列里面的直线是关于极角单调的。

由于队列里面直线的单调性,每次加入一条直线后队头和队尾一定是最可能会被切割的部分,这时候只要检查队尾和队头是否被加入的直线所切割,相应地弹出队尾和对头。

然后把加进来的直线从队尾入队时,特判和当前队尾的直线是否平行,如果平行,则保留内部直线,否则直接入队。

计算加入直线和之前队尾直线的交点。

当所有直线加入后根据队头删除无用的队尾直线。

VecLine L[maxN + 2];//半平面(为有向直线左侧)
Point p[maxN + 2];//多边形交集的顶点。
int HalfPlaneIntersection()
{
    int l = 1, r = 0, i = 1;
    for (q[++r] = L[i++]; i <= tot; ++i)//tot是直线的个数
    {
        while (l < r and L[i].Right(p[r - 1])) r--;//队尾直线的“起点”在新直线的右侧,弹出队尾
        while (l < r and L[i].Right(p[l])) l++;//队头直线的“起点”在新直线的右侧,弹出队头
        if (L[i].ang != q[r].ang)//判断新直线和队尾直线是否平行
            q[++r] = L[i];
        else if (L[i].Right(q[r].P))//保留内侧直线
            q[r] = L[i];
        if (l < r)
            p[r - 1] = Intersection(q[r], q[r - 1]);//计算交点
    }
    while (l < r and q[l].Right(p[r - 1])) r--;//合并队尾队头。
    if (r - l <= 1) return 0;//空集
    p[r] = Intersection(q[r], q[l]);//计算队头队尾的交点。
}

转载于:https://www.cnblogs.com/cnyali-Tea/p/11539649.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值