C++
 Computer >> コンピューター >  >> プログラミング >> C++

C++でマトリックス(行列)内の2つのセル間にパスが存在するかを判定する方法

本記事では、与えられたマトリックス(行列)の中に、2つのセルをつなぐパス(経路)が存在するかどうかを判定するC++プログラムについて解説します。

ここでは、0・1・2・3のいずれかの値を持つ正方行列が与えられたと仮定します。各値の意味は以下の通りです。

  • 0:空白の壁(通過不可)
  • 1:スタート地点(ソース)
  • 2:ゴール地点(デスティネーション)
  • 3:空白セル(通過可能)

マトリックス内にはソースとデスティネーションがそれぞれ1つだけ存在します。このプログラムの目的は、上下左右の4方向のみに移動し(斜め移動は禁止)、ソースからデスティネーションへ到達できる経路があるかどうかを確認することです。

解決のアプローチ

この問題は、マトリックスをグラフとして捉えることで効率的に解けます。各セルをグラフの頂点とみなし、隣接する通行可能なセル同士を辺で結びます。その後、幅優先探索(BFS)を用いて、ソースからデスティネーションへの到達可能性を調べます。

具体的な手順は以下の通りです。

  1. マトリックスの各セルに一意の番号(頂点ID)を割り当てます。
  2. 通行可能なセル(値が0以外)について、上下左右の隣接セルが通行可能であれば、対応する頂点間に辺を追加します。
  3. 値が1のセルをソース、値が2のセルをデスティネーションとして記録します。
  4. BFSを実行し、ソースからデスティネーションに到達できれば「Possible」、そうでなければ「Not Possible」を出力します。

C++コード例

#include<bits/stdc++.h>
using namespace std;

// 与えられた配列からグラフを作成するクラス
class use_graph {
    int W;
    list <int> *adj;
    public :
    use_graph( int W ){
        this->W = W;
        adj = new list<int>[W];
    }
    void add_side( int source , int dest );
    bool search ( int source , int dest);
};

// 辺を追加する関数
void use_graph :: add_side ( int source , int dest ){
    adj[source].push_back(dest);
    adj[dest].push_back(source);
}

// BFS(幅優先探索)を実行する関数
bool use_graph :: search(int source, int dest) {
    if (source == dest)
        return true;
    // 訪問済みフラグの初期化
    bool *visited = new bool[W];
    for (int i = 0; i < W; i++)
        visited[i] = false;
    list<int> queue;
    // ソースを訪問済みとしてマークし、キューに追加
    visited[source] = true;
    queue.push_back(source);
    // 隣接する頂点へ移動
    list<int>::iterator i;
    while (!queue.empty()){
        source = queue.front();
        queue.pop_front();
        for(i=adj[source].begin();i!=adj[source].end(); ++i) {
            if (*i == dest)
                return true;
            if (!visited[*i]) {
                visited[*i] = true;
                queue.push_back(*i);
            }
        }
    }
    // デスティネーションに到達できなかった場合
    return false;
}

// 移動先のセルが有効かどうかを判定する関数
bool is_okay(int i, int j, int M[][4]) {
    if ((i < 0 || i >= 4) || (j < 0 || j >= 4 ) || M[i][j] == 0)
        return false;
    return true;
}

// パスの存在を確認するメイン関数
bool find(int M[][4]) {
    int source , dest ;
    int W = 4*4+2;
    use_graph g(W);
    int k = 1 ;
    for (int i =0 ; i < 4 ; i++){
        for (int j = 0 ; j < 4; j++){
            if (M[i][j] != 0){
                if ( is_okay ( i , j+1 , M ) )
                    g.add_side ( k , k+1 );
                if ( is_okay ( i , j-1 , M ) )
                    g.add_side ( k , k-1 );
                if (j < 4-1 && is_okay ( i+1 , j , M ) )
                    g.add_side ( k , k+4 );
                if ( i > 0 && is_okay ( i-1 , j , M ) )
                    g.add_side ( k , k-4 );
            }
            if( M[i][j] == 1 )
                source = k ;
            if (M[i][j] == 2)
                dest = k;
            k++;
        }
    }
    return g.search (source, dest) ;
}

int main(){
    int M[4][4] = { { 0 , 3 , 0 , 1 }, { 3 , 0 , 3 , 3 }, { 2 , 3 , 0 , 3 },{ 0 , 0 , 3 , 0 }};
    (find(M) == true) ?
    cout << "Possible" : cout << "Not Possible" <<endl ;
    return 0;
}

出力結果

Not Possible

コードの解説

上記の例では、4×4のマトリックスに対して処理を行っています。関数find()では、まず各セルに番号kを割り当てながら、通行可能な隣接セルとの間に辺を張っていきます。セルの値が1ならソース、2ならデスティネーションとして記録します。

補助関数is_okay()は、指定された座標がマトリックスの範囲内であり、かつ壁(値0)でないことを確認します。これにより、範囲外アクセスや壁の通過を防いでいます。

最後に、クラスuse_graphsearch()メソッドがBFSを実行します。キューを使って頂点を順に処理し、デスティネーションに到達した時点でtrueを返します。キューが空になっても到達できなければ、パスは存在しないと判断されfalseが返されます。

計算量

  • 時間計算量: O(N × M) — N×Mのマトリックスの全セルを一度ずつ処理するため
  • 空間計算量: O(N × M) — グラフの隣接リストと訪問済みフラグの保存に必要

この手法はBFSを使用しているため、最短経路の探索にも応用できます。また、DFS(深さ優先探索)やUnion-Find(素集合データ構造)を使っても同様の問題を解くことができます。

  1. C++で二分木の2つのノード間の距離を求めるプログラムの作成方法

    この記事では、二分木(バイナリツリー)と木の中の2つのノードが与えられたときに、それらのノード間の距離を求めるC++プログラムの作成方法を解説します。問題の概要ここでいう「2つのノード間の距離」とは、一方のノードからもう一方のノードへ移動する際に通過する辺(エッジ)の最小本数のことを指します。具体例を挙げて、問題を確認してみましょう。入力二分木Node1 = 3、Node2 = 5出力3出力の解説ノード3からノード5までの経路は「3 → 1 → 2 → 5」です。この経路で通過する辺は3本であるため、2つのノード間の距離は3となります。解法のアプローチこの問題に対する基本的な解法は、最小共通祖

  2. C++でグラフ行列の逆行列を求めるプログラムの実装方法

    本記事では、グラフ行列の逆行列を求めるC++プログラムについて詳しく解説します。行列の逆行列は、対象となる行列が正則(非特異)である場合、すなわち行列式が0ではない場合にのみ存在します。 逆行列を求める手法は複数ありますが、ここでは随伴行列(余因子行列の転置)と行列式を組み合わせて逆行列を計算する方法を紹介します。 アルゴリズムの手順 逆行列を求める際の処理の流れは以下の通りです。 開始 行列の逆行列を求める関数 INV() を定義する。 行列式を計算する関数 DET() を呼び出す。 随伴行列を生成する関数 ADJ() を呼び出す。 次の公式に基づいて逆行