C++でマトリックスの右下セルに到達するための最小ステップ数を求める方法
問題の概要
正の整数で構成された2次元マトリックス(行列)が与えられたとします。このとき、左上のセル (0, 0) から右下のセル (n-1, n-1) まで移動するために必要な最小ステップ数を求めるのが目的です。
移動のルールは以下のとおりです。現在いるセルを (i, j) とすると、次のいずれかのセルに移動できます。
- (i, j + mat[i][j]) … 現在のセルの値の分だけ右へ移動
- (i + mat[i][j], j) … 現在のセルの値の分だけ下へ移動
ただし、マトリックスの範囲を超えて移動することはできません。
入力例
例として、次のようなマトリックスを考えてみましょう。
| 2 | 1 | 2 |
| 1 | 1 | 1 |
| 1 | 1 | 1 |
この場合の出力は 2 となります。最短経路は以下のようになります。
(0, 0) → (0, 2) → (2, 2)
アプローチ:動的計画法(DP)
この問題は動的計画法(Dynamic Programming)を使うことで効率的に解くことができます。
現在いるセルを (i, j)、目標のセルを (n-1, n-1) としたとき、次のような漸化式が成り立ちます。
DP[i, j] = 1 + min(DP[i + arr[i][j], j], DP[i, j + arr[i][j]])
つまり、「そのセルからゴールまでの最小ステップ数」は、「ジャンプ先の2つのセルのうち小さい方の値 + 1(現在の移動分)」で表されます。すでに計算済みのセルの結果はメモ化しておくことで、同じ計算を繰り返さずに済みます。
C++での実装例
#include<iostream>
#define N 3
using namespace std;
int table[N][N];
bool temp_val[N][N];
int countSteps(int i, int j, int arr[][N]) {
if (i == N - 1 and j == N - 1)
return 0;
if (i > N - 1 || j > N - 1)
return INT_MAX;
if (temp_val[i][j])
return table[i][j];
temp_val[i][j] = true;
table[i][j] = 1 + min(countSteps(i + arr[i][j], j, arr), countSteps(i, j + arr[i][j], arr));
return table[i][j];
}
int main() {
int arr[N][N] = { { 2, 1, 2 }, { 1, 1, 1 }, { 1, 1, 1 } };
int ans = countSteps(0, 0, arr);
if (ans >= INT_MAX)
cout << -1;
else
cout << "Number of steps: " << ans;
}実行結果
Number of steps: 2
コードのポイント
- table[][]:各セルからゴールまでの最小ステップ数を保存するメモ化テーブルです。
- temp_val[][]:そのセルがすでに計算済みかどうかを記録するフラグ配列です。再計算を防ぎ、計算量を大幅に削減します。
- 境界チェック:マトリックスの範囲外に出た場合は INT_MAX を返し、到達不可能であることを示します。最終的に結果が INT_MAX 以上であれば、ゴールに到達できないため -1 を出力します。
このように、再帰とメモ化を組み合わせた動的計画法を用いることで、全経路を総当たりするよりもはるかに効率的に最小ステップ数を求めることができます。
-
C++で対戦相手を捕まえるために必要な最小ラウンド数を求めるプログラム
問題の概要 木構造の辺のリストが [u, v] の形式で与えられるとします。これは頂点 u と頂点 v の間に無向辺が存在することを表しています。さらに、2つの整数 x と y も与えられます。自分は頂点 x におり、対戦相手は頂点 y に位置しています。ゲームは第1ラウンドに自分が移動し、次のラウンドで対戦相手が移動するという形で交互に進行します。対戦相手は、自分の番に移動せずその場にとどまることも選択できます。このとき、対戦相手を捕まえるために必要な最小ラウンド数を求めるのが課題です。 たとえば、入力が edges = [[0, 1], [0, 2], [1, 3], [1, 4]]、x
-
C#で配列の末尾に到達するために必要な最小ジャンプ回数を求める方法
この問題は、配列の先頭要素からスタートし、そこから到達可能なすべての要素に対して再帰的に同じ処理を呼び出すことで解くことができます。先頭から配列の末尾に到達するまでの最小ジャンプ回数は、「先頭から到達可能な各要素から末尾へ到達するのに必要な最小ジャンプ回数」をもとにして計算できます。例として、次の配列を考えてみましょう。Array == {1, 3, 6, 3, 2, 3, 6, 8, 9, 5};この場合、末尾に到達するために必要なジャンプ回数は 4 回 です。アルゴリズムの考え方現在位置にある値が「その位置から何歩先までジャンプできるか」を表しています。そこで、ジャンプ可能な範囲内の各候補