C++でNxNグリッドの最小下降パスの合計を求める方法
問題文
N×Nのサイズを持つ整数行列 A が与えられます。この課題では、A を通る「下降パス(Falling Path)」の最小合計を求めることが目標です。
- 下降パスは、最初の行の任意の要素から開始し、最後の行で終了します。
- パスは次の行ごとに1つの要素を選択していきます。ただし、次の行で選ぶ要素の列は、前の行で選んだ列と同じ列、または左右に1つだけ隣接する列でなければなりません。
例
N = 2、行列が以下の場合:
{
{5, 10},
{25, 15}
}
出力は 20 となります(要素 5 と 15 が選択されるため)解法の考え方(動的計画法)
この問題は動的計画法(DP)を用いることで効率的に解けます。下から2番目の行から順に上へ向かって処理し、各セルに対して「そのセルから最下段まで到達する場合の最小合計」を計算していきます。具体的には、各セルの値に、真下・左下・右下の3つのセルのうち最小の値を加算します。すべての行を処理し終えた後、最初の行の中で最小の値が答えとなります。
C++実装例
#include <bits/stdc++.h>
#define MAX 2
using namespace std;
int getMinSumPath(int matrix[MAX][MAX]) {
for (int row = MAX - 2; row >= 0; --row) {
for (int col = 0; col < MAX; ++col) {
int val = matrix[row + 1][col];
if (col > 0) {
val = min(val, matrix[row + 1][col - 1]);
}
if (col + 1 < MAX) {
val = min(val, matrix[row + 1][col + 1]);
}
matrix[row][col] = matrix[row][col] + val;
}
}
int result = INT_MAX;
for (int i = 0; i < MAX; ++i)
result = min(result, matrix[0][i]);
return result;
}
int main() {
int matrix[MAX][MAX] = {
{5, 10},
{25, 15},
};
cout << "Minimum sum path = " << getMinSumPath(matrix)
<< endl;
return 0;
}上記のプログラムをコンパイルして実行すると、次の出力が得られます。
出力
Minimum sum path = 20
計算量
- 時間計算量:O(N²) — 行列のすべてのセルを一度ずつ処理するためです。
- 空間計算量:O(1) — 入力の行列をそのまま更新して結果を保持するため、追加のメモリは不要です。
-
C++で解くパス合計III:DFSで二分木のルートからリーフまでの経路を探索
整数のキーを持つノードで構成される二分木が与えられたとき、合計値が指定した値と一致する「ルートからリーフ(葉)までの経路」をすべて見つける問題を考えます。経路は必ず根から始まり、葉で終わる必要があります。 問題の例 たとえば、次のような二分木 [5,4,8,11,null,13,4,7,2,null,null,5,1] があり、目標の合計値が 22 だとします。 このとき、条件を満たす経路は次の 2 本です。 [[5, 4, 11, 2], [5, 8, 4, 5]] 解法のアプローチ:DFS(深さ優先探索)+バックトラック この問題は、少し手を加えた DFS(深さ優先探索)関数で効率的に解
-
C++で数値Nを回文の和として表すために必要な最小の回文の個数を求める方法
問題の概要数値Nが与えられたとき、Nをいくつかの回文(上から読んでも下から読んでも同じ並びになる数)の和として表すために必要な回文の最小個数を求める問題です。例えば、N = 15の場合、15 = 8 + 7 と表現できるため、必要な回文の個数は2となります。アルゴリズムの考え方この問題は次の2つのステップで解くことができます。N以下のすべての回文を昇順に生成する和がちょうどNになるような最小の部分集合のサイズを求める後半のステップはいわゆる「部分和問題」の一種であり、メモ化再帰(動的計画法)を用いることで効率的に解けます。回文の効率的な生成方法すべての数値に対して回文かどうかを1つずつ判定する