目次

2次元平面上の凸多角形の共通部分

2つの凸多角形同士の共通部分は、存在すれば必ず1つの凸多角形になる。
(凹多角形だと複数に分かれることがある)

凸多角形を $P,Q$ とし、$((x_{p1},y_{p1}),...,(x_{pN},y_{pN}))$ のように反時計回りの点列で与えられるとする。
$P$ の点の個数を $N$、$Q$ の点の個数を $M$ とする。

共通部分の凸多角形を、同様に点列で求めたい。

アルゴリズム

$O(NM)$ アルゴリズム

共通部分の頂点を蓄積する集合 $Rset$ を用意する。

それぞれ $O(NM)$ ずつかかる。
これで、$Rset$ には共通部分の全頂点が入った。

これをちゃんと順番通りに並べる。
まず重心を求める。
凸多角形の重心は必ず多角形内にあるので、重心からの偏角でソートすれば、順番通りになる。

$O(N+M)$ アルゴリズム

以下、点同士の比較は $(x,y)$ 基準でおこなうものとする。($x$ 優先、$x$ が同じなら $y$ で比較)

凸包を求めるアルゴリズムの1つMonotone Chainのように、上辺と下辺に分けて考える。

$P$ を最小の頂点 $p_{min}$ から最大の頂点 $p_{max}$ まで、 $P$ の上側を伝う頂点列と下側を使う頂点列に分ける。
ただし $p_{min}$ と $p_{max}$ は両方の最初・最後に重複して入っているとする。

$Q$ も同じようにし、共通部分も上と下に分けて求めるとすると、それらを構成する頂点は以下のいずれかになる。

実装上は、たとえば以下で管理する。

どちらかのlo,hiがともに $p_{max}$ または $q_{max}$ に到達すれば終了。

最初は $i,j,k,l=-1$ などとしておくとよい。
一方の頂点が進められたとき、他方がまだ -1 ならindexだけ進めてスキップする。

実装例

同じ点が連続しないようにしている。
面倒だったので、最初に $N+M$ 点をまとめてソートしているので、計算量には $\log$ がつく。

一応、共通部分の面積を求める以下の問題でVerifyしているが、 面積だと多少の誤差は許されてしまうので、直接的に合っているかは未Verify。

ネタバレ注意

特に小数点誤差によっては間違った結果を返す可能性がある。
とはいえ、与えられる $P,Q$ の座標が整数であれば、小数点が出現するのは交点のみであり、 小数点同士の四則演算などは行わないので $x,y \le 10^5$ くらいは大丈夫、、、なはず。

Python3