Competitive Programming Note
108 師大附中校隊培訓
本文已停止更新,新版請至 WiwiHo 的競程筆記
你有一大堆點,然後你要找出一個可以圍住這些點且面積最小的凸多邊形,這個凸多邊形稱為凸包。
顯而易見,如果要面積最小,那凸包的頂點勢必得是這一大堆點的其中幾個點,你也可以想成是用一條橡皮筋把這些點圈起來。
那麼,如何有效率地把凸包蓋出來呢?
先把各個點按 座標由小到大排序, 座標相同則再按 座標由小到大排序,排序之後的點順序會是由左至右、由上至下,這樣一來我們就可以按這個順序遍歷這些點,這種往固定方向掃描的方式,稱為掃描線。
知道點的左右上下關係可以幹嘛?
先討論一件事情:有一個凸多邊形,它的頂點已經按逆時針順序排好了,依序是 ,那麼 和 與 的關係會是什麼(令 )?既然是凸多邊形,那麼它的邊應該要是一直往同個方向轉彎的,而如果將邊逆時針排序,這個邊的斜率也應該是一直往逆時針方向轉彎,顯然點也會是這樣,因此:
再來,我們把凸包分成上下兩部分:上凸包和下凸包,以極左和極右點分割,如果極左點或極右點有兩個(最多只會有兩個),上面那個屬於上凸包,下面那個屬於下凸包,否則極左點必屬於下凸包,極右點必屬於上凸包。
顯然,上或下凸包中不會有 座標相同的頂點,因此在上或下凸包中,每個頂點都能分別出左右的關係,並且你會發現,如果把點按逆時針排序,在下凸包中的點也是由左而右排序的、在上凸包的點也是由右而左排序的。
這樣一來左右關係就有用了,先用由左而右的掃描線把下凸包做出來,再用由右而左的掃描線把上凸包做出來,就可以得到整個凸包。
這整個流程可以用一個 stack 來實作,在處理一個點的時候,我們嘗試把它加進凸包裡,此時這個點是 ,堆頂的點是 ,堆頂再往下一個點是 ,把這些點代入剛剛的式子,符合條件或者 stack 大小小於 時就停止,否則就把堆頂 pop,然後繼續重複,結束之後就把目前處理中的點放入堆頂。
在做下凸包的時候,先從最左邊且最下面的點開始做上述動作,做到最後,堆頂的點應該會是最右邊且最上面的點,把它 pop 掉,因為它應該屬於上凸包;做上凸包的時候,從最右邊且最上面的點開始做,最後堆頂會是最左且最下的點,把它 pop 掉後,這兩個接起來就是完整的凸包。
因為要用到堆頂往下一個點,所以 stack 用 vector 來實作。
這個演算法叫 Andrew's monotone chain,另一種比較常聽到的凸包演算法是 Graham's scan,有興趣可以自己查。