Pythonでボードを正方形に分割する最小コストを求めるアルゴリズム
問題の概要
縦 p、横 q のサイズを持つ1枚のボードがあるとします。このボードを p×q 個の正方形に切り分けるとき、切断にかかる総コストをできるだけ小さくしたいと考えます。それぞれの切断線には個別のコストが設定されており、その値があらかじめ与えられています。
例として、横方向の切断コストが X_slice = [3,2,4,2,5]、縦方向の切断コストが Y_slice = [5,2,3] の場合を考えてみましょう。

この場合、出力される最小コストは 65 となります。
解法のアプローチ(貪欲法)
この問題は貪欲法(Greedy Algorithm)を使って効率的に解くことができます。ポイントとなる考え方は次のとおりです。
- コストの高い切断線ほど早い段階で実行すると、その後の切断回数が増えても影響を受ける部分が少なくて済む。
- したがって、X方向・Y方向のすべてのコストを降順にソートし、大きい方から順に処理していく。
- ある時点での切断を実行するたびに、すでに存在する反対方向のピース数を掛けてコストに加算する。
アルゴリズムの手順
- res := 0(累積コスト)、horizontal := 1、vertical := 1、i := 0、j := 0 で初期化する。
- i < m かつ j < n の間、以下を繰り返す。
- X_slice[i] > Y_slice[j] の場合:res += X_slice[i] × vertical とし、horizontal を +1、i を +1 する。
- それ以外の場合:res += Y_slice[j] × horizontal とし、vertical を +1、j を +1 する。
- 残った X_slice の合計 total を求め、res += total × vertical を加算する。
- 残った Y_slice の合計 total を求め、res += total × horizontal を加算する。
- 最終的な res を返す。
Pythonでの実装例
それでは、上記のロジックを実際のPythonコードで確認してみましょう。
def minCost(X_slice, Y_slice, m, n):
res = 0
X_slice.sort(reverse=True)
Y_slice.sort(reverse=True)
horizontal = 1
vertical = 1
i = 0
j = 0
while i < m and j < n:
if (X_slice[i] > Y_slice[j]):
res += X_slice[i] * vertical
horizontal += 1
i += 1
else:
res += Y_slice[j] * horizontal
vertical += 1
j += 1
total = 0
while (i < m):
total += X_slice[i]
i += 1
res += total * vertical
total = 0
while (j < n):
total += Y_slice[j]
j += 1
res += total * horizontal
return res
m = 6; n = 4
X_slice = [3,2,4,2,5]
Y_slice = [5,2,3]
print(minCost(X_slice, Y_slice, m-1, n-1))入力
[3,2,4,2,5],[5,2,3]
出力
65
計算量について
このアルゴリズムでは、最初のソート処理が O(m log m + n log n)、その後のマージ処理が O(m + n) となるため、全体の時間計算量はソートの部分が支配的になります。貪欲法により、各切断のコストを「現在存在する交差するピース数」と掛け合わせていくことで、常に局所的に最適な選択を行い、結果として全体の最小コストを達成できます。
-
Pythonで都市を最小コストで接続する方法|クラスカル法とUnion-Findによる実装
問題概要 1からNまでの番号が付けられたN個の都市があるとします。接続情報connectionsの各要素は[city1, city2, cost]という形式で与えられ、これはcity1とcity2を直接つなぐためのコストを表します。ここで求めたいのは、任意の2つの都市の間に必ず経路が存在する状態(全域木)を作るときの最小コストです。コストは採用した接続のコストの合計であり、すべての都市を接続できない場合は-1を返します。 たとえば、次のようなグラフが与えられたとします。 この場合の出力は6になります。3つの都市をすべてつなぐには2本の接続で十分なので、コストの小さい組み合わせ、すなわち[2
-
Pythonで解く「葉の値から構成する最小コスト二分木」問題 ― メモ化再帰による動的計画法
問題の概要 正の整数からなる配列 arr が与えられたとき、次の条件をすべて満たす二分木を考えます。 各ノードは、子を 0 個または 2 個持つ。 配列 arr の値は、木の中間順巡回(inorder traversal)における各葉の値に対応する。 各非葉ノードの値は、左部分木と右部分木それぞれにおける最大の葉の値の積と等しい。 考えられるすべての二分木の中から、各非葉ノードの値の合計が最小となるものを見つけるのが目的です。例えば、入力 arr = [6, 2, 4] の場合、出力は 32 になります。この配列からは次の 2 通りの木が構成できます。 上の図では、非葉ノードの値(24