C++で指定した開始文字から最長の連続パスの長さを求める方法
異なる文字が格納された行列(マトリックス)が与えられます。ある文字を起点として、現在の文字より1つ大きい連続した文字(例:a→b→c→d)をたどりながら、最長のパスの長さを見つけることが課題です。移動は、縦・横・斜めを含む8方向の隣接セルに対して可能です。
例えば、下図のような行列が与えられ、開始文字を「E」とします。

この行列で開始文字「e」から探索すると、最長の連続パスの長さは5となります。
アルゴリズムの考え方
最長パスを見つけるには、深さ優先探索(DFS)アルゴリズムを使用します。DFSの実行中には、同じ部分問題が何度も発生することがあります。このような部分問題を繰り返し計算しないようにするため、動的計画法(メモ化)のアプローチを採用します。一度計算した結果を longestPath 配列にキャッシュすることで、計算量を大幅に削減できます。
C++での実装例
#include<iostream>
#define ROW 3
#define COL 3
using namespace std;
// 隣接セルを再帰的に探索するための8方向の移動量
int x[] = {0, 1, 1, -1, 1, 0, -1, -1};
int y[] = {1, 0, 1, 1, -1, -1, 0, -1};
int longestPath[ROW][COL];
char mat[ROW][COL] = {
{'a','c','d'},
{'h','b','a'},
{'i','g','f'}
};
int max(int a, int b){
return (a>b)?a:b;
}
bool isvalid(int i, int j){
if (i < 0 || j < 0 || i >= ROW || j >= COL) // i と j が範囲外の場合
return false;
return true;
}
bool isadjacent(char previous, char current){
return ((current - previous) == 1); // 文字が連続しているかチェック
}
int findLongestLen(int i, int j, char prev){
if (!isvalid(i, j) || !isadjacent(prev, mat[i][j]))
// 範囲外または連続していない場合
return 0;
if (longestPath[i][j] != -1)
return longestPath[i][j]; // 部分問題はすでに解決済み
int len = 0; // 結果を 0 で初期化
for (int k=0; k<8; k++) // 最長パスの長さを再帰的に求める
len = max(len, 1 + findLongestLen(i + x[k], j + y[k], mat[i][j]));
return longestPath[i][j] = len; // 長さを保存して返す
}
int getLen(char start){
for(int i = 0; i<ROW; i++)
for(int j = 0; j<COL; j++)
longestPath[i][j] = -1; // すべての要素を -1 で初期化
int len = 0;
for (int i=0; i<ROW; i++){
for (int j=0; j<COL; j++){ // すべての開始候補をチェック
if (mat[i][j] == start) {
for (int k=0; k<8; k++) // 8方向の隣接セルをすべて調べる
len = max(len, 1 + findLongestLen(i + x[k], j + y[k], start));
}
}
}
return len;
}
int main() {
char start;
cout << "Enter Starting Point (a-i): "; cin >> start;
cout << "Maximum consecutive path: " << getLen(start);
return 0;
}
出力結果
Enter Starting Point (a-i): e Maximum consecutive path: 5
コードの解説
isvalid():指定されたセルが行列の範囲内にあるかどうかを判定します。範囲外の場合は false を返します。
isadjacent():現在の文字が前の文字より文字コードで1つ大きいか、つまり連続した文字(例:a→b)になっているかをチェックします。
findLongestLen():指定セルから再帰的に最長パスを探索する核心的な関数です。すでに計算済みのセル(longestPath[i][j] != -1)は、保存された結果をそのまま返すことで再計算を回避します。
getLen():行列内で開始文字と一致するすべてのセルを起点として探索を行い、得られた最長の長さを返します。
計算量について
メモ化により各セルの計算は一度だけ行われるため、時間計算量は O(ROW × COL × 8)、すなわち O(ROW × COL) となります。動的計画法を用いない素朴なDFSでは、最悪の場合に指数時間かかる可能性がありますが、メモ化を組み合わせることで効率的に解くことができます。
-
C++でグラフに長さkを超える単純パスが存在するか判定するアルゴリズム
概念 重み付きグラフ、グラフ内の始点となる頂点、そして数値k(kは始点から終点までのパスの長さを表します)が与えられたとき、与えられた始点から始まり、任意の他の頂点(終点)で終わる単純パス(閉路を含まないパス)が存在するかどうかを判定することが課題です。以下のグラフを使って説明します。 入力例1 始点 s = 0, k = 64 出力 True この場合、0 -> 7 -> 1 -> 2 -> 8 -> 6 -> 5 -> 3 -> 4 という合計距離68の単純パスが存在し、64を超えているためtrueとなります。 入力例2 始点 s = 0
-
C++で依存関係からタスクの実行順序を見つける方法(トポロジカルソート)
問題概要n個の異なるタスクがあるとします。各タスクには0からn-1までのラベルが付けられており、一部のタスクには前提条件(先に完了しておく必要のあるタスク)が存在します。例えば、タスク2を選択したい場合は、まずタスク1を完了していなければなりません。この関係はペア [2, 1] として表現されます。タスクの総数と前提条件ペアのリストが与えられたとき、すべてのタスクを完了できるような実行順序を見つける必要があります。有効な順序が複数存在する場合は、そのうちのどれか1つを返せば構いません。また、与えられたすべてのタスクを完了することが不可能な場合(循環依存が存在する場合)は、空の配列を返します。例