Pythonでコインマトリックスから収集できる最大コイン数を求めるプログラム
問題の概要
各セル matrix[r, c] にコインが置かれている 2 次元マトリックスを考えます。あるセル matrix[r, c] のコインを回収すると、上下の行(r - 1 行目と r + 1 行目)にあるすべてのコイン、さらに左右に隣接するセル matrix[r, c + 1] と matrix[r, c - 1] のコインが消えてしまいます。この制約のもとで、回収できるコインの合計の最大値を求めるのが目的です。
たとえば、入力が次のような場合を考えてみましょう。
| 2 | 8 | 7 | 6 |
| 10 | 10 | 4 | 2 |
| 5 | 9 | 2 | 3 |
この場合の出力は 26 になります。8・6・9・3 のコインが置かれたセルを選べば、合計が 26 となるためです。
解法のアプローチ
この問題は、いわゆる「家を襲う(House Robber)」型の動的計画法を 2 段階に分けて適用することで効率的に解けます。
- 行単位の処理: 同じ行内では、あるセルを選ぶと左右隣のセルが利用できなくなるため、「隣接する要素を同時に選ばない」という制約下での最大和を、各行について個別に求めます。
- 行間の処理: ある行のセルを選ぶと上下の行がすべて消えるため、行ごとの結果に対して再び同じ動的計画法を適用し、マトリックス全体の最大値を導きます。
アルゴリズムの手順
- 関数 getmax(arr) を定義します。「隣接要素を同時に選ばない」条件下での最大和を返す関数です。
- prev_max := 0、curr_max := 0、res := 0 で初期化します。
- arr 内の各 num について、次の処理を繰り返します。
- temp := curr_max
- curr_max := num + prev_max
- prev_max := max(temp, prev_max)
- res := max(res, curr_max)
- res を返します。
- メインの solve() では、以下の手順を実行します。
- matrix が空の場合は 0 を返します。
- m := 行数、n := 列数を取得します。
- サイズ m の配列 row_sum を 0 で初期化します。
- i = 0 ~ m - 1 の範囲で、row_sum[i] := getmax(matrix[i]) を計算します。
- 最後に getmax(row_sum) を返します。
Python 実装例
理解を深めるために、以下の実装例を見てみましょう。
def getmax(arr):
prev_max, curr_max = 0, 0
res = 0
for num in arr:
temp = curr_max
curr_max = num + prev_max
prev_max = max(temp, prev_max)
res = max(res, curr_max)
return res
def solve(matrix):
if not matrix:
return 0
m, n = len(matrix), len(matrix[0])
row_sum = [0 for _ in range(m)]
for i in range(m):
row_sum[i] = getmax(matrix[i])
return getmax(row_sum)
matrix = [
[2, 8, 7, 6],
[10, 10, 4, 2],
[5, 9, 2, 3]
]
print(solve(matrix))入力
[ [2, 8, 7, 6], [10, 10, 4, 2], [5, 9, 2, 3] ]
出力
26
計算量
時間計算量は O(m × n)、空間計算量は O(m) です。各行を一度ずつ走査した後、行ごとの結果に対して一度だけ動的計画法を適用するだけでよいため、大規模なマトリックスにも対応できる非常に効率的な解法といえます。
-
【Python】2値行列の列を反転して、すべての値が等しくなる行の最大数を求める方法
問題の概要2値行列(各要素が0または1のみで構成される行列)が与えられたとします。この行列に対しては、任意の数の列を選択し、その列に含まれるすべてのセルの値を反転することができます。ここでいう「反転」とは、0を1に、1を0に切り替える操作のことです。このとき、いくつかの列を反転した後、すべての値が等しくなる行の最大数を求めるのがこの問題の目的です。例として、次のような行列を考えてみましょう。000001110この場合の出力は 2 になります。最初の2つの列を反転すると、2行目は「1,1,1」、3行目は「0,0,0」となり、これら2行のすべての値が等しくなるためです。解法のアプローチこの問題を解
-
【Python】各アイテムを何度でも選べるナップサック問題で最大価値を求めるプログラム
問題概要同じ長さを持つ2つのリスト weights(重さ)と values(価値)、および整数 capacity(容量)が与えられます。weights[i] と values[i] は、それぞれ i 番目のアイテムの重さと価値を表します。ここで特別なルールとして、各アイテムは何個でも(何度でも)選んでよいものとします。合計の重さが capacity を超えない範囲でアイテムを選ぶとき、得られる価値の合計の最大値を求めるのがこの問題です。これは「無制限ナップサック問題(Unbounded Knapsack Problem)」として知られる古典的な動的計画法の応用例です。たとえば、次の入力を考えて