Dominando o Problema House Robber: Estratégias de Programação Dinâmica em Diferentes Estruturas

O problema "House Robber" é um clássico de programação dinâmica que explora a tomada de decisão otimizada sob restrições de adjacência. Abordaremos três variações fundamentais: a sequência linear de casas, a disposição circular e a estrutura organizada em árvore binária.

1. House Robber: Sequência Linear

Neste cenário básico, temos um array de valores representando o dinheiro em cada casa. A única restrição é que não podemos asslatar duas casas adjacentes. Para resolver isso, definimos dp[i] como o valor máximo acumulado até a casa i.

A lógica de transição baseia-se em duas escolhas para a casa atual:

  1. Não assaltar a casa atual: O valor acumulado será o mesmo da casa anterior (dp[i-1]).
  2. Assaltar a casa atual: O valor será a soma do valor da casa atual com o acumulado até duas casas atrás (dp[i-2] + nums[i]).
class Solution {
    public int rob(int[] casas) {
        if (casas == null || casas.length == 0) return 0;
        int totalCasas = casas.length;
        if (totalCasas == 1) return casas[0];

        int[] lucroMaximo = new int[totalCasas];
        lucroMaximo[0] = casas[0];
        lucroMaximo[1] = Math.max(casas[0], casas[1]);

        for (int i = 2; i < totalCasas; i++) {
            // Decisão: pular a casa atual ou somar a casa atual com o lucro de i-2
            lucroMaximo[i] = Math.max(lucroMaximo[i - 1], lucroMaximo[i - 2] + casas[i]);
        }

        return lucroMaximo[totalCasas - 1];
    }
}

2. House Robber II: Disposição Circular

Nesta variação, as casas estão dispostas em um círculo, o que significa que a primeira e a última casa são vizinhas. Para resolver essa depandência circular, quebramos o problema em dois subproblemas lineares:

  • Considerar apenas as casas da primeira até a penúltima (índice 0 a n-2).
  • Considerar apenas as casas da segunda até a última (índice 1 a n-1).

O resultado final será o valor máximo entre essas duas possibilidades.

class Solution {
    public int rob(int[] nums) {
        int n = nums.length;
        if (n == 1) return nums[0];
        if (n == 0) return 0;

        return Math.max(
            calcularMaximo(nums, 0, n - 2),
            calcularMaximo(nums, 1, n - 1)
        );
    }

    private int calcularMaximo(int[] lista, int inicio, int fim) {
        if (inicio == fim) return lista[inicio];
        
        int anterior = 0;
        int atual = 0;
        
        for (int i = inicio; i <= fim; i++) {
            int temporario = atual;
            atual = Math.max(atual, anterior + lista[i]);
            anterior = temporario;
        }
        
        return atual;
    }
}

3. House Robber III: Estrutura em Árvore

Nesta versão, as casas estão coenctadas como nós de uma árvore binária. Se um nó pai for assaltado, os filhos diretos não podem ser. A abordagem mais eficiente aqui é utilizar uma travessia de pós-ordem (bottom-up), onde cada nó retorna um array de dois elementos:

  • indice 0: Valor máximo se não assaltarmos o nó atual.
  • indice 1: Valor máximo se assaltarmos o nó atual.
class Solution {
    public int rob(TreeNode raiz) {
        int[] resultado = explorar(raiz);
        return Math.max(resultado[0], resultado[1]);
    }

    private int[] explorar(TreeNode no) {
        // [0] não rouba, [1] rouba
        if (no == null) return new int[]{0, 0};

        int[] esquerda = explorar(no.left);
        int[] direita = explorar(no.right);

        // Se roubarmos o nó atual, não podemos roubar os filhos
        int roubarAtual = no.val + esquerda[0] + direita[0];

        // Se não roubarmos o nó atual, podemos escolher o máximo de cada filho
        int pularAtual = Math.max(esquerda[0], esquerda[1]) + Math.max(direita[0], direita[1]);

        return new int[]{pularAtual, roubarAtual};
    }
}

A complexidade de tempo para as três abordagens é O(n), onde n representa o número de casas ou nós, garantindo uma execução eficiente mesmo para grandes volumes de dados.

Tags: Dynamic Programming java Binary Tree algorithms

Publicado em 6-15 19:22 por Thomas