Python
 Computer >> コンピューター >  >> プログラミング >> Python

Pythonでボードを正方形に分割する最小コストを求めるアルゴリズム

問題の概要

縦 p、横 q のサイズを持つ1枚のボードがあるとします。このボードを p×q 個の正方形に切り分けるとき、切断にかかる総コストをできるだけ小さくしたいと考えます。それぞれの切断線には個別のコストが設定されており、その値があらかじめ与えられています。

例として、横方向の切断コストが X_slice = [3,2,4,2,5]、縦方向の切断コストが Y_slice = [5,2,3] の場合を考えてみましょう。

Pythonでボードを正方形に分割する最小コストを求めるアルゴリズム

この場合、出力される最小コストは 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) となるため、全体の時間計算量はソートの部分が支配的になります。貪欲法により、各切断のコストを「現在存在する交差するピース数」と掛け合わせていくことで、常に局所的に最適な選択を行い、結果として全体の最小コストを達成できます。

  1. Pythonで都市を最小コストで接続する方法|クラスカル法とUnion-Findによる実装

    問題概要 1からNまでの番号が付けられたN個の都市があるとします。接続情報connectionsの各要素は[city1, city2, cost]という形式で与えられ、これはcity1とcity2を直接つなぐためのコストを表します。ここで求めたいのは、任意の2つの都市の間に必ず経路が存在する状態(全域木)を作るときの最小コストです。コストは採用した接続のコストの合計であり、すべての都市を接続できない場合は-1を返します。 たとえば、次のようなグラフが与えられたとします。 この場合の出力は6になります。3つの都市をすべてつなぐには2本の接続で十分なので、コストの小さい組み合わせ、すなわち[2

  2. Pythonで解く「葉の値から構成する最小コスト二分木」問題 ― メモ化再帰による動的計画法

    問題の概要 正の整数からなる配列 arr が与えられたとき、次の条件をすべて満たす二分木を考えます。 各ノードは、子を 0 個または 2 個持つ。 配列 arr の値は、木の中間順巡回(inorder traversal)における各葉の値に対応する。 各非葉ノードの値は、左部分木と右部分木それぞれにおける最大の葉の値の積と等しい。 考えられるすべての二分木の中から、各非葉ノードの値の合計が最小となるものを見つけるのが目的です。例えば、入力 arr = [6, 2, 4] の場合、出力は 32 になります。この配列からは次の 2 通りの木が構成できます。 上の図では、非葉ノードの値(24