半平面交初步

半平面交:

問題簡述:

給定一些半平面,求他它們交集(大小,周長,\(\cdots\))。算法

半平面的表示:

半平面一般由一些關於直線的不等式給出,好比 \(y\le kx + b\) 半平面就是直線 \(y = kx + b\) 下方的一塊區域。spa

而若是咱們欽定半平面在有向直線的左側,那麼咱們就能夠經過一條有向直線來表示半平面。code

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)\) 的在線算法,每次增長一條直線就嘗試去切割半平面交的多邊形,具體實現咕咕咕。隊列

極角排序(離線)

首先把全部直線極角排序,維護一個雙端隊列,隊列裏面存了當前半平面交的多邊形上的直線以及多邊形上的交點。io

而且隊列裏面的直線是關於極角單調的。class

因爲隊列裏面直線的單調性,每次加入一條直線後隊頭和隊尾必定是最可能會被切割的部分,這時候只要檢查隊尾和隊頭是否被加入的直線所切割,相應地彈出隊尾和對頭。while

而後把加進來的直線從隊尾入隊時,特判和當前隊尾的直線是否平行,若是平行,則保留內部直線,不然直接入隊。co

計算加入直線和以前隊尾直線的交點。oss

當全部直線加入後根據隊頭刪除無用的隊尾直線。

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]);//計算隊頭隊尾的交點。
}
相關文章
相關標籤/搜索