# 110 選手班 - 計算幾何 ###### tags: `宜中資訊` `CP` Ccucumber12 2021.08.25 --- ## Outline - 2D-Vector - Segments - Sort by Angle - Convex Hull - Duality of Points and Lines --- ## 2D-Vector ---- ### 向量運算 - 加法 - 減法 - 乘法 - 除法 ---- ```c= using GT = long long ; struct Pt { GT x, y ; Pt operator+(const Pt b) const { return Pt{x+b.x, y+b.y} ; } Pt operator-(const Pt b) const { return Pt{x-b.x, y-b.y} ; } Pt operator*(const GT b) const { return Pt{x*b, y*b} ; } Pt operator/(const GT b) const { return Pt{x/b, y/b} ; } bool operator==(const Pt b) const { return x == b.x && y == b.y ; } double len () { return sqrt(x*x + y*y) ; } GT norm () { return x*x + y*y ; } }; ``` ---- ### Dot - 內積 - $(x_1, y_1) \cdot (x_2, y_2)=x_1x_2 + y_1y_2$ - 兩個向量在同一方向的程度 ---- ### Cross - 外積 - $(x_1, y_1) \times (x_2, y_2)=x_1y_2 - x_2y_1$ - 兩個向量的垂直程度,逆時鐘為正 ---- ```c= GT dot(Pt a, Pt b) { return a.x * b.x + a.y * b.y ; } GT cross(Pt a, Pt b) { return a.x * b.y - a.y * b.x ; } ``` ---- ### Orientation - 兩個向量的方向性 - 逆時針:1 - 順時針:-1 - 平行:0 - cross ---- ```c= int ori(Pt a, Pt b) { GT ret = cross(a, b) ; if(ret == 0) return 0 ; return ret < 0 ? -1 : 1 ; } ``` ---- ### Area - 兩個夾角形成有向面積 - 逆時針:正數 - 順時針:負數 - $\vec{A} 與 \vec{B}$ 的夾角面積:$\vec{A} \times \vec{B}$ ---- 多邊形面積 - 切成到原點的多個三角形 - 依照點的順序計算有向面積加總 ---- ![](https://i.imgur.com/C9RNLy2.png) --- ## Segments ---- ### In Segment - 給定一個點及線段 - 請判斷點是否在線段內 ---- - 線段 $(A,B)$,點 $P$ - 直線上:$\text{cross}(\overrightarrow{AB}, \overrightarrow{AP}) = 0$ - 線段內:$\text{dot}(\overrightarrow{PA}, \overrightarrow{PB}) \leq 0$ ---- ```c= bool in_segment(Pt a, Pt b, Pt p){ return ori(b-a, p-a) == 0 && dot(p-a, p-b) <= 0 ; } ``` ---- ### Segment Intersection - 給定兩個線段 - 請判斷兩個線段是否相交 ---- - 線段 $(A,B)$,$(C,D)$ - 只有一個點在線段內:in_segment - 嚴格相交:將彼此切割成兩半 - $\text{ori}(\overrightarrow{AB}, \overrightarrow{AC}) \times \text{ori}(\overrightarrow{AB}, \overrightarrow{AD}) < 0$ - $\text{ori}(\overrightarrow{CD}, \overrightarrow{CA}) \times \text{ori}(\overrightarrow{CD}, \overrightarrow{CB}) < 0$ ---- ```c= bool banana(Pt a, Pt b, Pt c, Pt d) { if( in_segment(a, b, c) || in_segment(a, b, d) || in_segment(c, d, a) || in_segment(c, d, b)) return true ; return ori(b-a, c-a) * ori(b-a, d-a) < 0 && ori(d-c, a-c) * ori(d-c, b-c) < 0 ; } ``` --- ## Sort by Angle ---- ### Trigonometric Functions - $\cos(\theta)$ \ $\sin(\theta)$ \ $\tan(\theta)$ - $\theta$ 參數為弧度制 - Time > $\mathcal{O}(1)$ - 用泰勒展開式等實作 ---- ### atan2 - Returns the principal value of the arc tangent of y/x, expressed in radians. --cplusplus.com - 回傳值域:$(-\pi, \pi]$ ![](https://i.imgur.com/Laou3IY.png) ---- ### Solution 1 - sort by atan2 ```c= bool cmp(Pt a, Pt b) { return atan2(a.x, a,y) < atan2(b.x, b.y) ; } ``` ---- - 優點:直觀、簡單 - 缺點:慢、浮點數誤差 ---- ### Solution 2 - sort by cross ```c= bool cmp(Pt a, Pt b) { bool f1 = a.x < 0 || (a.x == 0 && a.y < 0) ; bool f2 = b.x < 0 || (b.x == 0 && b.y < 0) ; if(f1 != f2) return f1 < f2 ; return cross(a, b) > 0 ; } ``` ---- - 切成兩半,各自外積判斷 - 優點:快速、正確 - 缺點:麻煩 --- ## Convex Hull ---- ### Convex - 凸多邊形 - 圖形內任兩點連線皆在圖形內部 - 所有內角小於 $180$ 度 ---- ![](https://i.imgur.com/TYsb4Vy.png) ---- ### Convex Hull - 包含所有點的最小凸多邊形 ![](https://i.imgur.com/Ehm0U6k.png) ---- ### Andrew's Monotone Chain - 將所有點依照 $(x,y)$ 排序 - 把下凸包圍出來 - 把上凸包圍出來 - 合併上下凸包 ---- ![](https://i.imgur.com/ZKXMIRs.png) ---- ### 圍下凸包 - 兩條邊的內角小於 $180$ 度 - 點 $A, B, C$ 依照 $(x,y)$ 排序 - 若 $\text{cross}(\overrightarrow{AB}, \overrightarrow{BC}) \leq 0$ 則 $B$ 不在凸包上 ---- - 以 stack 維護目前凸包 - 對於新加入的點,檢查前一個點是否在凸包上 - 下凸包:$0 \rightarrow N$ 逆時針旋轉 - 上凸包:$N \rightarrow 0$ 逆時針旋轉 ---- https://upload.wikimedia.org/wikipedia/commons/9/9a/Animation_depicting_the_Monotone_algorithm.gif ---- ### Time Complexity - Sort:$\mathcal{O}(N\log N)$ - Construct:$\mathcal{O}(N)$ - Total:$\mathcal{O}(N\log N)$ ---- ```c= vector<Pt> convex(vector<Pt> v){ vector<Pt> ret ; sort(v.begin(), v.end()) ; int k = 0 ; for(auto i:v){ while(k >= 2 && ori(ret[k-1] - ret[k-2], i - ret[k-1]) <= 0) ret.pop_back(); ret.push_back(i) ; ++k ; } v.pop_back(); int hf = --k ; reverse(v.begin(), v.end()); for(auto i:v){ while(k - hf >= 2 && ori(ret[k-1] - ret[k-2], i - ret[k-1]) <= 0) ret.pop_back(); ret.push_back(i) ; ++k ; } return ret; } ``` ---- ### Application - 最大值問題 - 最遠點對 - 最小包覆矩形 - 旋轉卡尺 - 邊斜率單調 - 最大三角形 ---- http://www-cgrl.cs.mcgill.ca/~godfried/research/calipers.html ---- ### 整數點限制 - 座標範圍 $[0,C]$ - 凸包上至多 $\mathcal{O}(\sqrt C)$ 個點 - 直接暴力枚舉最遠點對 ---- ### 直線與凸包 - 把一堆直線丟進平面 - 若只關心最上方的函數,將會形成一個下凸包 ---- ![](https://i.imgur.com/IYJ23wN.png) ---- ### Envelope - 快速尋找某個 $x$ 在多個線性函數下的最大值 - Find $\max(f_i(x)=a_ix+b_i)$ - 凸包上的函數斜率具有單調性 - 對函數三分搜 $\mathcal{O}(\log N)$ - DP 斜率優化 --- ## Duality of Points and Lines ---- ### Problem - 給定 $N$ 個點 - 請支援以下詢問 $Q$ 次 - 給定一條直線 $f(x) = ax + b$,請問是否所有點皆在直線上方? - $N, Q \leq 10^6$ ---- - $\forall i,\ y_i \geq f(x_i) = ax_i + b$ - $-b \geq ax_i - y_i$ - $(-b) \geq x_i (a) - y_i$ - 點 $(a, -b)$ 在直線 $f(t) = x_i(t) - y_i$ 上方 ---- - 所有點 $(x_i, y_i)$ 在直線 $f(x) = ax+b$ 上方 - $\Rightarrow$ 點 $(a, -b)$ 在所有直線 $f_i(t) = x_i(t) - y_i$ 上方 - $\Rightarrow$ 點 $(a, -b)$ 在所有直線 $f_i(t)$ 所構造出的下凸包之上 ---- - 排序所有直線 $f_i(t)$:$\mathcal{O}(N\log N)$ - 每次詢問檢查點是否在凸包上方:$\mathcal{O}(\log N)$ - Total:$\mathcal{O}(N\log N + Q\log N)$ ---- ### Key - 對偶性質:點 $\Longleftrightarrow$ 直線 - 多個點與一條線 $\Longleftrightarrow$ 多條線與一個點 - 改變題目的性值以幫助解題 ---- ### 類題 - CF 678F --- ## Credit - 資訊之芽 - 演算法筆記 - Wikipedia - OI Wiki - IONC - https://ithelp.ithome.com.tw/m/articles/10210364 - https://codeforces.com/blog/entry/63823
{"metaMigratedAt":"2023-06-16T08:31:57.430Z","metaMigratedFrom":"Content","title":"110 選手班 - 計算幾何","breaks":true,"contributors":"[{\"id\":\"45262441-89e4-46ca-959b-c0d6fadb64e2\",\"add\":6409,\"del\":488}]"}
    117 views