Implementação do Algoritmo BFS em C++ para Caminho Mais Curto em Labirinto

Este artigo demonstra como utilizar a estrutura de dados fila (queue) e o algoritmo de Busca em Largura (BFS) em C++ para determinar o caminho mais curto dentro de um labirinto representado por uma grade.

Conceitos Fundamentais

Uma fila é uma coleção de elementos que segue o princípio FIFO (First-In, First-Out). A inserção ocorre em uma extremidade (traseira) e a remoção na outra (frontal). Esta estrutura é ideal para modelar processos que requerem a ordenação pela ordem de chegada, como a exploração por camadas realizada pelo BFS.

O algoritmo BFS explora todos os vizinhos de um nó antes de avançar para os vizinhos dos vizinhso. Em um grafo ou grade ponderada uniformemente (como um labirinto sem custos variados), o BFS garante a descoberta do caminho de menor número de arestas (passos) entre dois nós.

Definindo a Estrutura do Problema

Para resolver o labirinto, precisamos definir alguns componentes-chave:

  • Mapa do Labirinto: Uma matriz 2D indicando células livres (0) e obstáculos (1).
  • Fila para BFS: Para gerenciar os nós a serem explorados.
  • Matriz de Visitados: Para evitar a reexploração de nós.
  • Matriz de Pais: Para rastrear o caminho percorrido e permitir a reconstrução da rota.
  • Matriz de Direções: Para definir os movimentos possíveis (cima, baixo, esquerda, direita).

Incluímos as bibliotecas necessárias:

#include <iostream>
#include <queue>
#include <vector>
#include <algorithm>
using namespace std;

Definimos uma estrutura para representar coordenadas e as variáveis globais que descrevem o labirinto:

struct Coordenada {
    int lin, col;
    Coordenada(int l = 0, int c = 0) : lin(l), col(c) {}
};

const int LINHAS = 10;
const int COLUNAS = 10;
int labirinto[LINHAS][COLUNAS] = {
    {0, 0, 0, 0, 1, 0, 1, 0, 1, 0},
    {0, 1, 0, 0, 0, 1, 0, 0, 0, 0},
    {0, 0, 0, 1, 0, 0, 1, 1, 0, 0},
    {0, 1, 0, 0, 0, 1, 0, 0, 0, 0},
    {0, 1, 0, 1, 0, 0, 0, 1, 0, 0},
    {0, 0, 0, 0, 0, 1, 0, 1, 0, 0},
    {1, 1, 1, 1, 1, 1, 1, 1, 0, 1},
    {0, 0, 0, 0, 0, 1, 0, 0, 0, 0},
    {1, 1, 1, 1, 0, 0, 0, 1, 1, 1},
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
};

Coordenada origem(0, 0);
Coordenada destino(LINHAS - 1, COLUNAS - 1);
bool visitado[LINHAS][COLUNAS] = {false};
Coordenada pai[LINHAS][COLUNAS];
const int movimentos[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};

Implementação do Algoritmo BFS

A função encontrarCaminho encapsula a lógica do BFS. Ela retorna um vetor com as coordenadas do caminho mais curto da origem ao destino, ou um vetor vazio se não houver caminho.

vector<Coordenada> encontrarCaminho() {
    queue<Coordenada> fila;
    fila.push(origem);
    visitado[origem.lin][origem.col] = true;

    while (!fila.empty()) {
        Coordenada atual = fila.front();
        fila.pop();

        // Verifica se chegamos ao destino
        if (atual.lin == destino.lin && atual.col == destino.col) {
            vector<Coordenada> caminho;
            for (Coordenada c = destino; !(c.lin == origem.lin && c.col == origem.col); c = pai[c.lin][c.col]) {
                caminho.push_back(c);
            }
            caminho.push_back(origem);
            reverse(caminho.begin(), caminho.end());
            return caminho;
        }

        // Explora os vizinhos
        for (int i = 0; i < 4; ++i) {
            int nova_lin = atual.lin + movimentos[i][0];
            int nova_col = atual.col + movimentos[i][1];

            // Verifica limites, obstáculos e se já foi visitado
            if (nova_lin >= 0 && nova_lin < LINHAS &&
                nova_col >= 0 && nova_col < COLUNAS &&
                labirinto[nova_lin][nova_col] == 0 &&
                !visitado[nova_lin][nova_col]) {
                
                visitado[nova_lin][nova_col] = true;
                pai[nova_lin][nova_col] = atual;
                fila.push(Coordenada(nova_lin, nova_col));
            }
        }
    }
    return vector<Coordenada>(); // Caminho não encontrado
}

Execução e Saída do Programa

No main, invocamos a função e formatamos a saída para exibir o caminho encontrado, se existir.

int main() {
    vector<Coordenada> caminho = encontrarCaminho();

    if (caminho.empty()) {
        cout << "Não existe caminho até o destino." << endl;
    } else {
        cout << "Comprimento do caminho mais curto: " << caminho.size() - 1 << endl;
        cout << "\nPasso a passo:" << endl;
        for (size_t i = 0; i < caminho.size(); ++i) {
            cout << "(" << caminho[i].lin << ", " << caminho[i].col << ")";
            if (i < caminho.size() - 1) {
                cout << " -> ";
                if ((i + 1) % 5 == 0) cout << endl;
            }
        }
        cout << endl;
    }
    return 0;
}

O algoritmo garante a otimalidade porque explora o labirinto camada por camada. A primeira vez que o destino é removido da fila, o caminho até ele necessariamente é o de menor comprimento. A matriz pai permite reconstruir essa rota ótima retrocedendo do destino até a origem.

Tags: C++ bfs algoritmo estrutura-de-dados fila

Publicado em 7-11 10:45