【C++】DFS(深さ優先探索)を使って2次元マトリクス内の島の数を求める方法
問題の概要
この問題では、0と1のみで構成される2次元のバイナリ行列が与えられます。私たちのタスクは、DFS(深さ優先探索)を用いて、その行列の中にいくつの「島」が存在するかを求めることです。
ここでいう島とは、行列の中で上下左右だけでなく斜め方向にも隣接している、1つ以上の「1」の集まりのことを指します。
具体例で問題を理解しよう
入力 : bin[][] = {{ 1 0 0 0}
{0 1 0 1}
{0 0 0 0}
{0 0 1 0}}
出力 : 3説明:
この行列には以下の3つの島が存在します。
- bin00 と bin11(斜めに隣接しているため同じ島として扱われる)
- bin13
- bin32
解法のアプローチ
DFSを用いてこの問題を解くには、行列内の各要素について、周囲8方向(上下左右+斜め4方向)の隣接セルを再帰的に探索し、「1」が連続している領域をひとまとまりとして捉えます。具体的な手順は以下の通りです。
- すべてのセルを「未訪問」状態にした訪問管理用の配列を用意します。
- 行列を左上から順に走査し、値が「1」かつ未訪問のセルを見つけたら、そこからDFSを開始します。
- DFSでは、そのセルから到達可能なすべての連結した「1」を訪問済みとしてマークしていきます。
- DFSを開始した回数をカウントすれば、それがそのまま島の総数になります。
一度訪問したセルを二度と訪問しないよう管理することで、同じ島を重複してカウントすることを防げます。
C++での実装例
#include <bits/stdc++.h>
using namespace std;
#define ROW 4
#define COL 4
// 指定されたセルが探索可能かどうかを判定する関数
int canVisit(int bin[][COL], int row, int col, bool visited[][COL]) {
return (row >= 0) && (row < ROW) && (col >= 0) && (col < COL)
&& (bin[row][col] && !visited[row][col]);
}
// DFSによる探索処理
void DFS(int bin[][COL], int row, int col, bool visited[][COL]) {
// 周囲8方向への移動量
static int getNeighbourRow[] = { -1, -1, -1, 0, 0, 1, 1, 1 };
static int getNeighbourCol[] = { -1, 0, 1, -1, 1, -1, 0, 1 };
visited[row][col] = true;
for (int k = 0; k < 8; ++k)
if (canVisit(bin, row + getNeighbourRow[k], col + getNeighbourCol[k], visited))
DFS(bin, row + getNeighbourRow[k], col + getNeighbourCol[k], visited);
}
// 島の数を数える関数
int findIslandCount(int bin[][COL]) {
bool visited[ROW][COL];
memset(visited, 0, sizeof(visited));
int islandCount = 0;
for (int i = 0; i < ROW; ++i)
for (int j = 0; j < COL; ++j)
if (bin[i][j] && !visited[i][j]) {
DFS(bin, i, j, visited);
islandCount++;
}
return islandCount;
}
int main() {
int bin[][COL] = {{1, 0, 0, 0},
{0, 1, 0, 1},
{0, 0, 0, 0},
{0, 0, 1, 0}};
cout << "行列に存在する島の数は " << findIslandCount(bin);
return 0;
}出力結果
行列に存在する島の数は 3
計算量の分析
時間計算量:O(ROW × COL)。各セルは最大でも1回しか訪問されないためです。
空間計算量:O(ROW × COL)。訪問管理用の配列と、再帰呼び出しのためのスタック領域が必要になるためです。
-
C++で文字列の部分文字列の総数を求める方法を解説
この記事では、与えられた文字列から作成できる空でない部分文字列の個数を求める方法について解説します。入力 : string = "moon" 出力 : 10 説明 : 部分文字列は m、o、o、n、mo、oo、on、moo、oon、moon の 10 個です。 入力 : string = "yellow" 出力 : 21解法のアプローチ文字列の長さを n とします。上の例からも分かるように、考えられるすべての部分文字列の個数を求めるには、長さ n、(n-1)、(n-2)、(n-3)、……2、1 の部分文字列の個数を順に加算していく必要があります。部分文
-
C++で列車の停車駅の組み合わせ数を求める方法
地点XとYの間にはn個の中間駅があるとします。ここで、「どの2つの停車駅も隣り合わない」という条件のもとで、s個の駅に停車する列車の配置方法が何通りあるかを求める問題を考えてみましょう。この記事では、停車駅の組み合わせ数を求めるためのアプローチを段階的に詳しく解説します。この問題は、本質的には組合せ論の問題であり、s個の停車駅の選び方の総数を求めることになります。 問題を解くアプローチ まず具体例として、中間駅が8個あり、そのうち3個の駅に停車させたい場合を考えてみます。 n = 8, s = 3 このとき、列車が停車できない駅は(n − s)、つまり5個残ることになります。 停車できない