Monotone Minima と SMAWK

以下のような問題があるとする。

  • 例題1
    • $N \times M$ の2次元配列 $A$ がある
    • 各行毎に、その行の最小値を求めたい
A 
i\j 0 1 2 3
 0  3 1 4 1  → 1
 1  5 9 2 6  → 2
 2  5 3 5 8  → 3
 3  9 7 9 3  → 3

これは、通常なら、各行毎にチェックして $O(NM)$ かける他にない。

ところが、$A$ がある“都合の良い条件”を満たしていると、$O((N+M) \log{N})$ や $O(N+M)$ でできる。

Monotone性

「$i$ の増加に伴って、最小値を取るindex $j$ が単調増加」の時、$A$ は「Monotone」という。

Monotone
i\j 0  1  2  3  4
 0  9 [3] 4  8  5    $i$ の増加につれ、最小値をとる $j$ は右に
 1  5 [1] 9  2  6
 2  5  7  6 [4] 8
 3  8  7  9  3 [2]
 4  7  9  8  8 [6]

また、$A$ から行と列を抜き出した任意の部分行列 $A'$ がMonotoneであれば、$A$ は「Totally Monotone」という。

Totally Monotone
i\j  0   1   2   3   4
 0  [7] 11  20  24  50
 1  23 [13] 17  16  36
 2  47  22  21 [16] 31
 3  75  42  37 [25] 34

↓たとえば、i=[0,2,3], j=[1,2,4] を抜き出してみても...

i\j  1   2   4    部分行列でも
 0 [11] 20  50    Monotone性が保たれている
 2  22 [21] 31
 3  42  37 [34]

元の例題1に立ち返ると、$A$ が、

  • Monotone であれば、Monotone Minima により、$O(N+M \log{N})$
  • Totally Monotone であれば、SMAWK により、$O(N+M)$

で各行の最小値を求める方法がある。

Monotone判定

実際に競技プログラミングの問題として Monotone 性を利用するのが出てくる場合、 $A$ は何かしら規則的な方法で生成されるのが大半だろう。

その場合「Monge性」を持つことを使うことが多い(らしい)。
「Monge性を満たすならTotally Monotone性も満たす」という性質がある。

Monotone Minima

$i$ について半分ずつ範囲を狭めていく。

i\j  0 1 2 3 4 5 6 7 8 9    N=7  M=10 として
 0               x x x x
 1               x x x x    まず、Nの真ん中の i=3 について O(M) かけて最小値を探す
 2               x x x x
 3   # # # # #[#]# # # #    j=5 のときに最小値を取るとすると、
 4   x x x x x              Monotone性より、左下と右上は可能性を除外できる。
 5   x x x x x
 6   x x x x x              結果、左上と右上の2つの矩形が残る。

※ #:値を調べた箇所  [#]:調べた中での最小値  x:最小値の可能性がなくなった箇所


i\j  0 1 2 3 4 5 6 7 8 9    分割された2つの矩形で再帰的に同じことをする。
 0       x x x x - - - -    行 [0,1,2] の真ん中 i=1 と、
 1   #[#]# # # # - - - -    行 [4,5,6] の真ん中 i=5 について探索する
 2   x           - - - -    この探索幅は、1箇所のみ重複するが、あわせておよそ O(M) である。
 3   - - - - - - - - - -    
 4   - - - - -       x x    最小値を見つけたら、
 5   - - - - - # #[#]# #    同様に、それぞれの左下と右上は可能性を除外でき、4つの矩形が残る。
 6   - - - - - x x          
 
 ※ -:前回までで調べた or 可能性がなくなり、調べる必要が無くなった箇所


i\j  0 1 2 3 4 5 6 7 8 9    再帰的に同じことをする。
 0  [#]# - - - - - - - -    探索幅は、あわせて O(M) である。
 1   - - - - - - - - - -    
 2   - # #[#]# # - - - -    
 3   - - - - - - - - - -    log N 回繰り返すと、全ての i について最小値の位置が判明する。
 4   - - - - -[#]# # - -    
 5   - - - - - - - - - -
 6   - - - - - - - # #[#]

探索幅は $O(M)$ と言っているが、厳密には重複が発生している。その回数は、全体を通して $O(N)$ となる。よって、全体で $O(N+M \log{N})$ で求められる。

SMAWK

定数倍の関係で、必ずしも Monotone Minima より早いとは限らない?

Monotone Minima の方がわかりやすく、実装も容易で、計算量の差もlog1つだけなので、 競技プログラミングでは無理してこちらを使う必要性は薄いかも?

アルゴリズムの日本語の文章化は以下のブログが詳しい。

Python3 による実装

応用例

最小値畳み込み

Monotone Minima や SMAWK を使うと、$C_i=\min_j(A_j+B_{i-j})$ みたいな $C$ も、$A,B$ の性質次第で高速に求められる。

$B$ が下に凸な時

$B$ が下に凸な時、それを以下のように並べた行列はMongeである。

B = { 9, 4, 1, 0, 1, 4 }
↓
i\j  0  1  2  3  4  5  6 ...
 0   9 ∞ ∞ ∞ ∞ ∞ ∞      これはMonge
 1   4  9 ∞ ∞ ∞ ∞ ∞
 2   1  4  9 ∞ ∞ ∞ ∞
 3   0  1  4  9 ∞ ∞ ∞
 4   1  0  1  4  9 ∞ ∞
 5   4  1  0  1  4  9 ∞
 6  ∞  4  1  0  1  4  9
 7  ∞ ∞  4  1  0  1  4
 :

で、Mongeな行列に対し、列全体に同じ値を加算してもMonge性は保たれるという性質がある。

A = { 3, 1, 4, 1, 5, 9, 2 }

i\j  0  1  2  3  4  5  6
    +3 +1 +4 +1 +5 +9 +2
 0  12 ∞ ∞ ∞ ∞ ∞ ∞      これもMonge
 1   7 10 ∞ ∞ ∞ ∞ ∞
 2   4  5 13 ∞ ∞ ∞ ∞
 3   3  2  8 10 ∞ ∞ ∞
 4   4  1  5  5 14 ∞ ∞
 5   7  2  4  2  9 18 ∞
 6  ∞  5  5  1  5 13 11
 7  ∞ ∞  8  2  4 10  6

これは、$X_{i,j}=A_j+B_{i-j}$ の行列になっている。これで行毎に最小値を取ったものは、まさしく $C$ である。

$B$ のみが下に凸な場合、畳み込みの添字に注意。$X_{i,j}=A_{i-j}+B_j$ としてしまうと上手くいかない。

LARSCH

SMAWKは、あらかじめ全ての $A$ が(関数で部分的に求めるにしろ)わかっていないといけない。

しかし動的計画法で順番に求める必要がある場合に、それだと上手くいかないことがある。

LARSCHは、「$\arg\min_k A_{j,k}$ の値が求まって、はじめて $A_{i,j+1}$ の値を使用できる」場合でも求めることができる。

FIXME

programming_algorithm/dynamic_programming/monotone_minima_smawk.txt · 最終更新: 2024/04/19 by ikatakos
CC Attribution 4.0 International
Driven by DokuWiki Recent changes RSS feed Valid CSS Valid XHTML 1.0