給定一些半平面,求他它們交集(大小,周長,\(\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]);//計算隊頭隊尾的交點。 }