C++で迷路内の目的地に到達する経路の数を数える方法
問題の概要
迷路は行 × 列(row × col)の行列として表現され、障害物のあるセルは -1、通行可能なセルには -1 以外の値が格納されています。ゴールは、左上のセル arr[0][0] から出発し、右下のセル arr[row-1][col-1] に到達することです。ただし、許される移動は次の2方向のみです。
- 右への移動: arr[i][j] → arr[i][j+1]
- 下への移動: arr[i][j] → arr[i+1][j]
例で理解しよう
入力 − arr[row][col] = {{0, 0, 0}, {-1, -1, 0}, {0, 0, 0}}
出力 − 迷路で目的地に到達する方法の数: 1
説明:
0 1 2 0 0 0 0 1 -1 -1 0 2 0 0 0
到達できる経路は次の1通りだけです。
- (0,0) → (0,1) → (0,2) → (1,2) → (2,2)
入力 − arr[row][col] = {{0, 0, 0, 0}, {-1, 0, -1, 0}, {-1, 0, -1, 0}, {0, 0, 0, 0}}
出力 − 迷路で目的地に到達する方法の数: 2
説明:
0 1 2 3 0 0 0 0 0 1 -1 0 -1 0 2 -1 0 -1 0 3 0 0 0 0
到達できる経路は次の2通りです。
- (0,0) → (0,1) → (1,1) → (2,1) → (3,1) → (3,2) → (3,3)
- (0,0) → (0,1) → (0,2) → (0,3) → (1,3) → (2,3) → (3,3)
アルゴリズムの考え方
このアプローチでは、まずすべての 0 を 1 に置き換えます。これは「そのセルへは少なくとも1つの経路で到達できる」ことを意味します。続いて、迷路の行列をもう一度走査し、各セルに対して次のように処理を行います。セルが障害物(-1)であれば無視してスキップし、そうでなければ上のセル (i-1, j) と左のセル (i, j-1) の値を調べます。これらの値が 0 より大きければ(= そこから到達可能であれば)、その値を現在のセル (i, j) に加算します。この操作を繰り返すことで、セル (row-1, col-1) に目的地までの総経路数が蓄積されていきます。
処理の手順
- 入力配列 arr[row][col] を迷路として受け取ります。
- 関数 destination_maze(int arr[row][col]) は、迷路で目的地に到達する方法の数を返します。
- 出発点のセルが障害物(
-1)で塞がれている場合は、0 を返します。 - 最左列を上から順に走査し、値が 0 のセルを 1 に置き換えます。障害物にぶつかった時点でループを中断します(それより下のセルには到達できないため)。
- 同様に、最上行も左から順に走査し、値が 0 のセルを 1 に置き換えます。
- セル (1,1) から配列全体を再び走査します。arr[i][j] が -1 なら何もせずスキップします。
- arr[i-1][j](上のセル)または arr[i][j-1](左のセル)が 0 より大きければ、そこから現在のセルへ到達できるため、その値を加算します。
- 最終的に arr[row-1][col-1] の値が、目的地に到達する総経路数となります。
- その値が 0 より大きければそれを返し、そうでなければ 0 を返します。
サンプルコード
#include<bits/stdc++.h>
using namespace std;
#define row 3
#define col 3
int destination_maze(int arr[row][col]) {
if (arr[0][0] == -1) {
return 0;
}
for (int i = 0; i < row; i++) {
if (arr[i][0] == 0) {
arr[i][0] = 1;
} else {
break;
}
}
for (int i = 1; i < col; i++) {
if (arr[0][i] == 0) {
arr[0][i] = 1;
} else {
break;
}
}
for (int i = 1; i < row; i++) {
for (int j = 1; j < col; j++) {
if (arr[i][j] == -1) {
continue;
}
if (arr[i - 1][j] > 0) {
arr[i][j] = (arr[i][j] + arr[i - 1][j]);
}
if (arr[i][j - 1] > 0) {
arr[i][j] = (arr[i][j] + arr[i][j - 1]);
}
}
}
if (arr[row - 1][col - 1] > 0) {
return arr[row - 1][col - 1];
} else {
return 0;
}
}
int main() {
int arr[row][col] = {
{0, 0, 0},
{-1, -1, 0},
{0, 0, 0}
};
cout << "迷路で目的地に到達する方法の数: " << destination_maze(arr);
return 0;
}上記のコードを実行すると、次のような出力が得られます。
出力
迷路で目的地に到達する方法の数: 1
計算量について
このアルゴリズムは、迷路の全セルを高々2回走査するだけで済むため、時間計算量は O(row × col) となります。また、追加の配列を使わず入力配列自体を結果の保存に利用しているため、空間計算量も O(1)(入力を除く)と非常に効率的です。
-
C++で1×mサイズのタイルを使ってn×mの床を敷き詰める方法の数を数える
問題概要部屋の床の長さと幅を表す 2 つの整数 n と m が与えられます。この床をサイズ 1×m のタイルで敷き詰める方法が何通りあるかを数えることが目的です。入力例 1n=3 m=2出力例 11 x m サイズのタイルを使用して n x m の床を敷き詰める方法の数は:3説明下図のように、1×2 のタイル 3 枚を並べる方法が 3 通り存在します。入力例 2n=3 m=3出力例 21 x m サイズのタイルを使用して n x m の床を敷き詰める方法の数は:2説明1×3 のタイル 3 枚をすべて縦方向に並べる方法と、すべて横方向に並べる方法があり、合計 2 通りとなります。考え方(アプロー
-
【C++】長方形に含まれる正方形の総数を求めるアルゴリズムと実装
縦の長さL、横の幅B(L≥B)の長方形が与えられたとします。この記事では、L×Bの長方形の中にいくつの正方形が含まれているかを効率的に求める方法を解説します。 上の図は3×2の長方形の例です。この長方形には、2×2の正方形が2個、1×1の正方形が6個含まれています。 合計:6+2=8個 規則性を見つける まず、正方形だけで構成されたB×Bの図形について考えてみましょう。 サイズL×Bの長方形には、必ずL×B個の1×1の正方形が含まれます。 含まれる最大の正方形のサイズはB×Bです。 L=B=1の場合:正方形の数=1 L=B=2の場合:正方形の数=1+4=5(2×2が1個、1×1が4個) L