0Pricing
Coding Interview Prep · Aula

Caminhos Únicos e Soma Mínima de Caminho em Grades

Preencha uma tabela DP 2D para caminhos únicos com e sem obstáculos e adapte-a para minimizar a soma dos valores ao longo de um caminho.

Caminhos Únicos e Soma Mínima de Caminho em Grades é uma aula grátis de Coding Interview Prep no CoddyKit. Esta é a aula 1 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Coding Interview Prep, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Coding Interview Prep inclui 4 aulas no total.

Caminhos Únicos em uma Grade

Caminhos Únicos (LeetCode 62) pergunta: em uma grade m×n, quantos caminhos diferentes vão do canto superior esquerdo ao canto inferior direito se você só puder mover-se para a direita ou para baixo? Para uma grade 3×7, a resposta é 28. A ideia principal é que todo caminho até a célula (i,j) deve vir de (i-1,j) (acima) ou de (i,j-1) (à esquerda), o que fornece uma formulação natural de DP 2D.

# 3x7 grid: robot starts at (0,0), goes to (2,6)
# Must make exactly 2 down-moves and 6 right-moves
# Total moves = 8, choose 2 for down = C(8,2) = 28
import math
print('Unique paths 3x7:', math.comb(3+7-2, 3-1))  # 28
print('Unique paths 3x3:', math.comb(3+3-2, 3-1))  # 6
print('Unique paths 2x2:', math.comb(2+2-2, 2-1))  # 2

Tabela de DP 2D para Caminhos Únicos

Defina dp[i][j] = número de caminhos até a célula (i,j). A primeira linha e a primeira coluna são compostas apenas de 1s (há apenas uma maneira de chegar a qualquer célula da linha superior ou da coluna mais à esquerda). Para as outras células: dp[i][j] = dp[i-1][j] + dp[i][j-1]. Preencha a tabela linha por linha; a resposta é dp[m-1][n-1]. Complexidade de tempo: O(m×n); espaço: O(m×n), redutível a O(n).

def unique_paths(m, n):
    dp = [[1] * n for _ in range(m)]
    # First row and column stay as 1s (base cases)
    for i in range(1, m):
        for j in range(1, n):
            dp[i][j] = dp[i-1][j] + dp[i][j-1]
    return dp[m-1][n-1]

print(unique_paths(3, 7))  # 28
print(unique_paths(3, 3))  # 6
print(unique_paths(1, 1))  # 1 (already at destination)

Otimização de Espaço para O(n)

Como dp[i][j] depende apenas da linha atual e da linha anterior, você pode substituir a tabela 2D completa por um único vetor 1D. Inicialize todos os valores como 1 e, depois, para cada linha, atualize diretamente: dp[j] += dp[j-1]. Depois de processar a linha i, dp[j] contém o valor que correspondia a dp[i][j] na tabela 2D. Este é um padrão comum de otimização para problemas de DP 2D.

def unique_paths_1d(m, n):
    dp = [1] * n  # initial row: all 1s
    for i in range(1, m):
        for j in range(1, n):
            dp[j] += dp[j-1]  # dp[j] was dp[i-1][j], dp[j-1] is dp[i][j-1]
    return dp[n-1]

print(unique_paths_1d(3, 7))  # 28
print(unique_paths_1d(3, 3))  # 6

# Or use math for O(1)
import math
print(math.comb(3+7-2, 3-1))  # 28

Caminhos Únicos II: Obstáculos

Caminhos Únicos II (LeetCode 63) adiciona obstáculos (células marcadas com 1) à grade. Qualquer caminho que passe por um obstáculo é inválido, portanto dp[i][j] = 0 se obstacle[i][j] == 1. Caso contrário, a recorrência é a mesma: dp[i][j] = dp[i-1][j] + dp[i][j-1]. Se o início ou o fim estiver bloqueado, o resultado será imediatamente 0. Defina os casos base com cuidado — assim que aparecer um 1 na primeira linha ou coluna, todas as células subsequentes nessa linha ou coluna serão 0.

def unique_paths_with_obstacles(obstacle_grid):
    m, n = len(obstacle_grid), len(obstacle_grid[0])
    dp = [[0] * n for _ in range(m)]
    # First row
    for j in range(n):
        if obstacle_grid[0][j] == 1: break
        dp[0][j] = 1
    # First column
    for i in range(m):
        if obstacle_grid[i][0] == 1: break
        dp[i][0] = 1
    for i in range(1, m):
        for j in range(1, n):
            if obstacle_grid[i][j] == 0:
                dp[i][j] = dp[i-1][j] + dp[i][j-1]
    return dp[m-1][n-1]

grid = [[0,0,0],[0,1,0],[0,0,0]]
print(unique_paths_with_obstacles(grid))  # 2

Problema da Soma Mínima de Caminho

Soma Mínima de Caminho (LeetCode 64) pergunta: dada uma grade m×n preenchida com inteiros não negativos, encontre o caminho do canto superior esquerdo ao canto inferior direito que minimize a soma de todos os números ao longo do caminho (movendo-se apenas para a direita ou para baixo). Por exemplo, em [[1,3,1],[1,5,1],[4,2,1]], o caminho 1→3→1→1→1 produz soma 7. O estado de DP é o mesmo dos caminhos únicos, mas agora a recorrência usa o mínimo em vez da adição.

grid = [[1, 3, 1],
        [1, 5, 1],
        [4, 2, 1]]
# Optimal path: (0,0)→(0,1)→(0,2)→(1,2)→(2,2)
# Values:        1  +  3  +  1  +  1  +  1  = 7
print('Expected minimum path sum:', 7)

Implementação de DP da Soma Mínima de Caminho

Defina dp[i][j] = custo mínimo para chegar à célula (i,j). Caso base: dp[0][0] = grid[0][0]. Primeira linha: dp[0][j] = dp[0][j-1] + grid[0][j] (a única maneira é vir da esquerda). Primeira coluna: dp[i][0] = dp[i-1][0] + grid[i][0] (a única maneira é vir de cima). Caso geral: dp[i][j] = grid[i][j] + min(dp[i-1][j], dp[i][j-1]). Esta é uma tradução direta do princípio da otimalidade.

def min_path_sum(grid):
    m, n = len(grid), len(grid[0])
    dp = [[0]*n for _ in range(m)]
    dp[0][0] = grid[0][0]
    for j in range(1, n):  # first row
        dp[0][j] = dp[0][j-1] + grid[0][j]
    for i in range(1, m):  # first column
        dp[i][0] = dp[i-1][0] + grid[i][0]
    for i in range(1, m):
        for j in range(1, n):
            dp[i][j] = grid[i][j] + min(dp[i-1][j], dp[i][j-1])
    return dp[m-1][n-1]

grid = [[1,3,1],[1,5,1],[4,2,1]]
print(min_path_sum(grid))  # 7

Soma Mínima de Caminho no Próprio Lugar

Se você tiver permissão para modificar a grade de entrada, poderá atualizá-la no próprio lugar para evitar a alocação de uma tabela de DP separada. Isso reduz o espaço auxiliar para O(1) (além da entrada). Às vezes, entrevistadores perguntam sobre essa otimização — confirme se é permitido modificar a entrada antes de fazê-lo. Caso contrário, o truque do vetor deslizante 1D fornece espaço O(n) sem modificar a entrada.

def min_path_sum_inplace(grid):
    m, n = len(grid), len(grid[0])
    # Mutate in place
    for i in range(m):
        for j in range(n):
            if i == 0 and j == 0: continue
            if i == 0:
                grid[i][j] += grid[i][j-1]
            elif j == 0:
                grid[i][j] += grid[i-1][j]
            else:
                grid[i][j] += min(grid[i-1][j], grid[i][j-1])
    return grid[m-1][n-1]

import copy
grid = [[1,3,1],[1,5,1],[4,2,1]]
print(min_path_sum_inplace(copy.deepcopy(grid)))  # 7

Soma Mínima de Caminho em Triângulo

Triângulo (LeetCode 120) solicita a soma mínima de um caminho do topo até a base em um vetor triangular, em que cada passo leva a um número adjacente na linha abaixo. A DP de baixo para cima é a abordagem mais simples: comece na penúltima linha e, para cada célula, adicione o mínimo das duas células diretamente abaixo. Isso evita acompanhar índices iniciais e faz a resposta subir naturalmente até o vértice.

def minimum_total(triangle):
    # Bottom-up: start from second-to-last row
    dp = triangle[-1][:]  # copy of bottom row
    for row in range(len(triangle) - 2, -1, -1):
        for col in range(len(triangle[row])):
            dp[col] = triangle[row][col] + min(dp[col], dp[col+1])
    return dp[0]

triangle = [
    [2],
    [3, 4],
    [6, 5, 7],
    [4, 1, 8, 3]
]
print(minimum_total(triangle))  # 11 (2+3+5+1)

DP de Grade em uma Masmorra

Jogo da Masmorra (LeetCode 174) pergunta qual é a saúde inicial mínima necessária para resgatar uma princesa no canto inferior direito de uma grade com células negativas (dano) e positivas (cura). Você deve mover-se para a direita ou para baixo. O truque é preencher a tabela de DP de trás para frente (do canto inferior direito ao canto superior esquerdo), calculando a saúde mínima necessária em cada célula. Em cada célula: dp[i][j] = max(1, min(dp[i+1][j], dp[i][j+1]) - dungeon[i][j]). A saúde deve permanecer sempre em pelo menos 1.

def calculate_minimum_hp(dungeon):
    m, n = len(dungeon), len(dungeon[0])
    dp = [[0]*n for _ in range(m)]
    # Fill from bottom-right
    dp[m-1][n-1] = max(1, 1 - dungeon[m-1][n-1])
    for i in range(m-2, -1, -1):  # last column
        dp[i][n-1] = max(1, dp[i+1][n-1] - dungeon[i][n-1])
    for j in range(n-2, -1, -1):  # last row
        dp[m-1][j] = max(1, dp[m-1][j+1] - dungeon[m-1][j])
    for i in range(m-2, -1, -1):
        for j in range(n-2, -1, -1):
            need = min(dp[i+1][j], dp[i][j+1])
            dp[i][j] = max(1, need - dungeon[i][j])
    return dp[0][0]

dungeon = [[-2,-3,3],[-5,-10,1],[10,30,-5]]
print(calculate_minimum_hp(dungeon))  # 7

Comparando Problemas de DP em Grades

Os problemas de DP em grades compartilham a mesma estrutura, mas diferem na direção do preenchimento e na operação de transição: Caminhos Únicos usa adição (conta todas as maneiras). Soma Mínima de Caminho usa o mínimo (otimiza). Jogo da Masmorra é preenchido de trás para frente (saúde necessária a partir do futuro). Ao abordar uma nova DP em grade, pergunte-se: (1) O que cada célula representa? (2) Em que direção devo preencher? (3) Qual operação combina as células vizinhas? Responder a essas três perguntas revela a solução completa.

# Summary: Grid DP Patterns
#
# Problem          Fill Dir   Transition
# Unique Paths     top-left   dp[i][j] = dp[i-1][j] + dp[i][j-1]
# Unique Paths II  top-left   same but 0 if obstacle
# Min Path Sum     top-left   dp[i][j] = grid[i][j] + min(above, left)
# Triangle         bottom-up  dp[col] = row[col] + min(dp[col], dp[col+1])
# Dungeon          bottom-right max(1, min(right, down) - cell)

# Recognise the pattern, write the transition, verify with examples
print('Grid DP summary complete')

Resumo da Complexidade para DP em Grades

Todos os problemas de DP em grade apresentados aqui têm complexidade de tempo O(m×n). O espaço varia de O(m×n) para uma tabela completa até O(n) com um vetor deslizante 1D e O(1) de espaço auxiliar quando a grade pode ser modificada no próprio lugar. Em entrevistas, mencione a otimização de espaço O(n) depois de apresentar a solução O(m×n) — isso demonstra que você conhece os compromissos envolvidos. Para todos os problemas, considere também se existe um atalho guloso, como a fórmula matemática para caminhos únicos.

# O(n) space version of Min Path Sum
def min_path_sum_1d(grid):
    m, n = len(grid), len(grid[0])
    dp = [float('inf')] * n
    dp[0] = 0
    for i in range(m):
        dp[0] += grid[i][0]  # first column: only from above
        for j in range(1, n):
            dp[j] = grid[i][j] + min(dp[j], dp[j-1])
    return dp[n-1]

grid = [[1,3,1],[1,5,1],[4,2,1]]
print(min_path_sum_1d(grid))  # 7

Verificação Rápida

Teste sua compreensão dos conceitos de Estruturas de Dados e Algoritmos — Preparação para Entrevistas de Programação desta lição.

Resumo da Lição

Nesta lição, você aprendeu: Caminhos Únicos preenche uma tabela 2D com dp[i][j] = dp[i-1][j] + dp[i][j-1] e pode ser calculado em O(1) usando combinatória, Soma Mínima de Caminho usa a mesma estrutura, mas substitui a adição por min para obter o custo ótimo do caminho e todos os problemas de DP em grades compartilham o padrão de definir um estado por célula e escolher um operador de transição (soma, mínimo, máximo). Em seguida, exploraremos a Maior Subsequência Comum usando DP 2D em duas sequências.

Perguntas Frequentes

A aula “Caminhos Únicos e Soma Mínima de Caminho em Grades” é grátis?

Sim — o texto completo de “Caminhos Únicos e Soma Mínima de Caminho em Grades” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Coding Interview Prep, atualize para CoddyKit PRO. O curso de Coding Interview Prep inclui 4 aulas no total.

O que vou aprender em “Caminhos Únicos e Soma Mínima de Caminho em Grades”?

Preencha uma tabela DP 2D para caminhos únicos com e sem obstáculos e adapte-a para minimizar a soma dos valores ao longo de um caminho. Você pratica Coding Interview Prep com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar Coding Interview Prep?

Nenhuma experiência prévia é necessária. Coding Interview Prep no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 1 de 4.

Quanto tempo leva a aula “Caminhos Únicos e Soma Mínima de Caminho em Grades”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de Coding Interview Prep?

Sim. Cada aula de Coding Interview Prep inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. Caminhos Únicos e Soma Mínima de Caminho em Grades
  2. Maior Subse­quência Comum
  3. Distância de Edição (Levenshtein)
  4. Otimização de Espaço para DP 2D
← Voltar para Coding Interview Prep