Técnicas de DFS e Backtracking em Competições de Programação

Busca em Profundidade sem Backtracking para Acessibilidade

Em problemas de labirinto onde o objetivo é apenas verificar a existência de um caminho entre dois pontos, não é necessário restaurar o estado dos nós visitados. Utilizar uma abordagem de DFS que marca permanentemente as células visitadas evita a reexploração desnecessária. Isso reduz a complexidade de tempo exponencial para linear em relação ao tamanho do grid, O(N*M), impedindo que o algoritmo exceda o limite de tempo.

A implementação abaixo modifica o grid original para marcar as posições já processadas, eliminando a necessidade de uma matriz de visitados separada e do passo de backtracking.

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

int linhas, colunas;
vector<string> grid;
bool alcançado = false;
int dx[] = {1, -1, 0, 0};
int dy[] = {0, 0, 1, -1};

void navegar(int x, int y) {
    if (x == linhas && y == colunas) {
        alcançado = true;
        return;
    }
    
    for (int i = 0; i < 4; ++i) {
        int nx = x + dx[i];
        int ny = y + dy[i];
        
        if (nx >= 1 && nx <= linhas && ny >= 1 && ny <= colunas) {
            if (grid[nx][ny] == '.') {
                grid[nx][ny] = '#'; // Marca como visitado permanentemente
                navegar(nx, ny);
                if (alcançado) return;
            }
        }
    }
}

int main() {
    cin >> linhas >> colunas;
    grid.resize(linhas + 1);
    for (int i = 1; i <= linhas; ++i) {
        string linha;
        cin >> linha;
        grid[i] = " " + linha; // Índice 1-based
    }
    
    grid[1][1] = '#';
    navegar(1, 1);
    
    if (alcançado) cout << "Yes" << endl;
    else cout << "No" << endl;
    
    return 0;
}

Geração de Permutações via Recursão

Para listar todas as permutações possíveis de uma sequência numérica, pode-se utilizar uma função recursiva que constrói a solução passo a passo. Um vetor de controle indica quais números já foram utilizados na ramiifcação atual. Embora exista uma função nativa na biblioteca padrão, implementar a lógica manualmente demonstra o entendimento do mecanismo de backtracking.

#include<iostream>
#include<iomanip>
#include<vector>
using namespace std;

int n;
vector<int> resultado;
vector<bool> ocupado;

void gerarPermutacoes(int indice) {
    if (indice > n) {
        for (int i = 1; i <= n; ++i) {
            cout << setw(5) << resultado[i];
        }
        cout << endl;
        return;
    }
    
    for (int i = 1; i <= n; ++i) {
        if (!ocupado[i]) {
            ocupado[i] = true;
            resultado[indice] = i;
            gerarPermutacoes(indice + 1);
            ocupado[i] = false; // Backtracking
        }
    }
}

int main() {
    cin >> n;
    resultado.resize(n + 1);
    ocupado.assign(n + 1, false);
    gerarPermutacoes(1);
    return 0;
}

Contagem de Componentes Conectados em Grid

Este problema equivale a contar regiões conectadas em uma matriz bidimensional. A estratégia envolve iterar sobre cada célula e, ao encontrar um valor não nulo não visitado, iniciar uma DFS para marcar toda a região conectada como visitada. Cada iniciação representa um novo componente encontrado.

É crucial converter a entrada de caracteres para valores inteiros para facilitar a verificação lógica durante a travessia.

#include<iostream>
#include<cstring>
using namespace std;

int n, m, totalComponentes = 0;
int campo[105][105];
bool visitado[105][105];
int movX[] = {0, 0, 1, -1};
int movY[] = {1, -1, 0, 0};

void expandirCelula(int x, int y) {
    visitado[x][y] = true;
    for (int i = 0; i < 4; ++i) {
        int nx = x + movX[i];
        int ny = y + movY[i];
        
        if (nx > 0 && nx <= n && ny > 0 && ny <= m) {
            if (!visitado[nx][ny] && campo[nx][ny] != 0) {
                expandirCelula(nx, ny);
            }
        }
    }
}

int main() {
    cin >> n >> m;
    memset(visitado, 0, sizeof(visitado));
    
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= m; ++j) {
            char temp;
            cin >> temp;
            campo[i][j] = temp - '0';
        }
    }
    
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= m; ++j) {
            if (!visitado[i][j] && campo[i][j] != 0) {
                expandirCelula(i, j);
                totalComponentes++;
            }
        }
    }
    
    cout << totalComponentes << endl;
    return 0;
}

Problema das N-Rainhas com Otimização de Diagonais

A solução clássica para o problema das N-Rainhas requer verificação de conflitos em linhas, colunas e diagonais. Para otimizar a verificação de diagonais, utiliza-se a propriedade matemática onde células na mesma diagonal principal possuem diferença constante (linha - coluna) e células na mesma diagonal secundária possuem soma constante (linha + coluna).

Arrays booleanos são utilizados para marcar essas somas e diferenças, permitindo verificação em O(1) durante a recursão. Atenção especial deve ser dada aos índices dos arrays de diagonais para evitar acesso fora dos limites.

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

int n;
bool diagNeg[50], diagPos[50], linha[50];
vector<int> solucao;
int totalSolucoes = 0;
int exibirLimite = 3;

void posicionarRainha(int col) {
    if (col > n) {
        if (exibirLimite > 0) {
            for (int i = 1; i <= n; ++i) {
                cout << solucao[i] << " ";
            }
            cout << endl;
            exibirLimite--;
        }
        totalSolucoes++;
        return;
    }
    
    for (int r = 1; r <= n; ++r) {
        if (!linha[r] && !diagNeg[col - r + n] && !diagPos[col + r]) {
            linha[r] = true;
            diagNeg[col - r + n] = true;
            diagPos[col + r] = true;
            solucao[col] = r;
            
            posicionarRainha(col + 1);
            
            linha[r] = false;
            diagNeg[col - r + n] = false;
            diagPos[col + r] = false;
        }
    }
}

int main() {
    cin >> n;
    solucao.resize(n + 1);
    posicionarRainha(1);
    cout << totalSolucoes << endl;
    return 0;
}

Tags: dfs backtracking c-plus-plus problema-rainhas travessia-grafos

Publicado em 10-12 23:05