Combinação de Busca em Largura com Operações Módulo

No desenvolvimento de algoritmos para problemas de grafos, a marcação adequada dos nós visitados é essencial para garantir eficiência. Neste caso, utilizamos busca em largura (BFS) combinada com operações módulo, onde o array vis deve ser marcado no momento exato para evitar complexidade desnecessária.

A estratégia correta é marcar os nós assim que são inseridos na fila, não ao serem removidos. Isso porque, em BFS, a primeira vez que um nó é alcançado já representa o caminho mais curto. Se a marcação ocorrer apenas ao retirar da fila, múltiplas visitas ao mesmo nó podem aumentar o tempo de execução.

// Implementação BFS com marcação imediata
int bfs_otimizado() {
    fila.push({1, 1, a[1][1]});
    visitado[1][1][a[1][1]] = 1;
    while (!fila.empty()) {
        auto no_atual = fila.front();
        int linha = no_atual[0], coluna = no_atual[1], resto = no_atual[2];
        fila.pop();
        if (visitado[linha][coluna][resto]) continue;
        visitado[linha][coluna][resto] = 1;
        for (int direcao = 0; direcao < 4; direcao++) {
            int nova_linha = linha + desloc_x[direcao];
            int nova_coluna = coluna + desloc_y[direcao];
            if (nova_linha < 1 || nova_linha > n || nova_coluna < 1 || nova_coluna > m) continue;
            int novo_resto = (resto + a[nova_linha][nova_coluna]) % modulo;
            if (visitado[nova_linha][nova_coluna][novo_resto]) continue;
            visitado[nova_linha][nova_coluna][novo_resto] = 1;
            distancia[nova_linha][nova_coluna][novo_resto] = distancia[linha][coluna][resto] + 1;
            fila.push({nova_linha, nova_coluna, novo_resto});
        }
    }
    return (distancia[n][m][0] != 0) ? distancia[n][m][0] : -1;
}

Essa abordagem difere do algoritmo de Dijkstra, que utiliza uma fila de prioridade. Em Dijkstra, um nó pode ser inserido múltiplas vezes na fila com diferantes custos, e a marcação ocorre apenas quando ele é removido, garantindo que o custo mínimo seja processado primeiro.

// Algoritmo de Dijkstra para comparação
int dijkstra_modificado() {
    fila_prioridade.push({0, ponto_inicial});
    for (int i = 1; i <= total_nos; i++) visitado[i] = 0, custo_minimo[i] = infinito;
    while (!fila_prioridade.empty()) {
        int custo = -fila_prioridade.top().first, no = fila_prioridade.top().second;
        fila_prioridade.pop();
        if (visitado[no]) continue;
        visitado[no] = 1;
        for (int i = cabecalho[no]; i != -1; i = proximo[i]) {
            int vizinho = aresta[i];
            int novo_custo = peso[i] + custo;
            if (novo_custo < custo_minimo[vizinho]) {
                custo_minimo[vizinho] = novo_custo;
                fila_prioridade.push({-novo_custo, vizinho});
            }
        }
    }
    int maximo = 0;
    for (int i = 1; i <= total_nos; i++) {
        if (custo_minimo[i] != infinito) {
            maximo = max(maximo, custo_minimo[i]);
        }
    }
    return maximo;
}

O código completo para resolver o problema inclui leitura de entrada e tratamento de casos especiais, como quando o módulo é 2, onde o caminho mais curto é simplesmente a distância Manhattan.

// Solução completa
#include <bits/stdc++.h>
using namespace std;

const int MAX_LINHA = 15;
const int MAX_RESTO = 10002;
int linhas, colunas, modulo_entrada;
int grade[MAX_LINHA][MAX_LINHA];
int dist[MAX_LINHA][MAX_LINHA][MAX_RESTO];
int vis[MAX_LINHA][MAX_LINHA][MAX_RESTO];
int desloc_x[] = {-1, 0, 0, 1};
int desloc_y[] = {0, -1, 1, 0};

int bfs_solucao() {
    queue<array<int, 3>> fila_bfs;
    fila_bfs.push({1, 1, grade[1][1]});
    vis[1][1][grade[1][1]] = 1;
    while (!fila_bfs.empty()) {
        auto atual = fila_bfs.front();
        int x = atual[0], y = atual[1], z = atual[2];
        fila_bfs.pop();
        for (int d = 0; d < 4; d++) {
            int nx = x + desloc_x[d], ny = y + desloc_y[d];
            if (nx < 1 || nx > linhas || ny < 1 || ny > colunas) continue;
            int novo_z = (z + grade[nx][ny]) % modulo_entrada;
            if (vis[nx][ny][novo_z]) continue;
            vis[nx][ny][novo_z] = 1;
            dist[nx][ny][novo_z] = dist[x][y][z] + 1;
            fila_bfs.push({nx, ny, novo_z});
        }
    }
    return (dist[linhas][colunas][0] != 0) ? dist[linhas][colunas][0] : -1;
}

int main() {
    cin >> linhas >> colunas >> modulo_entrada;
    if (modulo_entrada == 2) {
        cout << linhas + colunas - 2 << endl;
        return 0;
    }
    modulo_entrada--;
    for (int i = 1; i <= linhas; i++) {
        for (int j = 1; j <= colunas; j++) {
            cin >> grade[i][j];
            grade[i][j] %= modulo_entrada;
        }
    }
    int descarte;
    for (int i = 1; i <= linhas; i++) {
        for (int j = 1; j <= colunas; j++) {
            cin >> descarte;
        }
    }
    cout << bfs_solucao() << endl;
    return 0;
}

Tags: Busca em Largura dijkstra operações módulo Grafos C++

Publicado em 6-9 18:25 por Thomas