0Pricing
DSA Interview Prep · Lección

Rutas únicas y suma mínima de rutas en cuadrículas

Rellene una tabla de DP 2D para rutas únicas con y sin obstáculos y adáptela después para minimizar la suma de valores a lo largo de una ruta.

Rutas únicas y suma mínima de rutas en cuadrículas es una lección gratuita de DSA Interview Prep en CoddyKit. Esta es la lección 1 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de DSA Interview Prep, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de DSA Interview Prep incluye 4 lecciones en total.

Unique Paths en una cuadrícula

Unique Paths (LeetCode 62) plantea la siguiente pregunta: en una cuadrícula de m×n, ¿cuántos caminos distintos van de la esquina superior izquierda a la esquina inferior derecha si solo puede moverse a la derecha o hacia abajo? Para una cuadrícula de 3×7, la respuesta es 28. La idea clave es que todo camino hasta la celda (i,j) debe proceder de (i-1,j) (arriba) o de (i,j-1) (izquierda), lo que da lugar a una formulación natural de DP bidimensional.

# 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

Tabla de DP bidimensional para Unique Paths

Defina dp[i][j] como el número de caminos hasta la celda (i,j). La primera fila y la primera columna contienen únicamente 1 (solo hay una forma de llegar a cualquier celda de la fila superior o de la columna situada más a la izquierda). Para las demás celdas: dp[i][j] = dp[i-1][j] + dp[i][j-1]. Rellene la tabla fila por fila; la respuesta es dp[m-1][n-1]. Complejidad temporal: O(m×n); espacio: O(m×n), reducible 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)

Optimización del espacio a O(n)

Dado que dp[i][j] solo depende de la fila actual y de la fila anterior, puede sustituir la tabla bidimensional completa por un único array unidimensional. Inicialice todos los valores a 1 y, para cada fila, actualice el array sobre el mismo sitio: dp[j] += dp[j-1]. Después de procesar la fila i, dp[j] contiene el valor que tenía dp[i][j] en la tabla bidimensional. Este es un patrón de optimización habitual en problemas de DP bidimensional.

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

Unique Paths II: obstáculos

Unique Paths II (LeetCode 63) añade obstáculos (celdas marcadas con 1) a la cuadrícula. Cualquier camino que atraviese un obstáculo no es válido, por lo que dp[i][j] = 0 si obstacle[i][j] == 1. En caso contrario, la recurrencia es la misma: dp[i][j] = dp[i-1][j] + dp[i][j-1]. Si el inicio o el final están bloqueados, el resultado es inmediatamente 0. Inicialice los casos base con cuidado: una vez que aparece un 1 en la primera fila o columna, todas las celdas posteriores de esa fila o columna son 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 de suma mínima de caminos

Minimum Path Sum (LeetCode 64) plantea la siguiente pregunta: dada una cuadrícula de m×n llena de enteros no negativos, encuentre el camino desde la esquina superior izquierda hasta la inferior derecha que minimice la suma de todos los números del camino (moviéndose únicamente a la derecha o hacia abajo). Por ejemplo, en [[1,3,1],[1,5,1],[4,2,1]], el camino 1→3→1→1→1 tiene suma 7. El estado de DP es el mismo que en Unique Paths, pero ahora la recurrencia utiliza el mínimo en lugar de la suma.

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)

Implementación de DP para la suma mínima de caminos

Defina dp[i][j] como el coste mínimo para llegar a la celda (i,j). Caso base: dp[0][0] = grid[0][0]. Primera fila: dp[0][j] = dp[0][j-1] + grid[0][j] (la única forma es llegar desde la izquierda). Primera columna: dp[i][0] = dp[i-1][0] + grid[i][0] (la única forma es llegar desde arriba). Caso general: dp[i][j] = grid[i][j] + min(dp[i-1][j], dp[i][j-1]). Se trata de una traducción directa del principio de optimalidad.

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

Suma mínima de caminos in situ

Si se permite modificar la cuadrícula de entrada, puede actualizarla in situ para evitar asignar una tabla de DP independiente. Esto reduce el espacio auxiliar a O(1) (sin contar la entrada). En ocasiones, los entrevistadores preguntan por esta optimización; aclare si se permite modificar la entrada antes de hacerlo. Si no se permite, el truco del array unidimensional rotativo ofrece un espacio O(n) sin modificar la 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

Suma mínima de caminos en un triángulo

Triangle (LeetCode 120) pide encontrar la suma mínima de un camino desde la parte superior hasta la inferior de un array triangular, donde cada paso lleva a un número adyacente de la fila inferior. La DP de abajo arriba es la opción más clara: comience en la penúltima fila y, para cada celda, sume el mínimo de las dos celdas situadas directamente debajo. Esto evita tener que hacer un seguimiento de los índices iniciales y hace que el resultado ascienda de forma natural hasta el vértice superior.

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 cuadrícula en una mazmorra

Dungeon Game (LeetCode 174) pide encontrar la salud inicial mínima necesaria para rescatar a una princesa en la esquina inferior derecha de una cuadrícula con celdas negativas (daño) y positivas (curación). Debe moverse a la derecha o hacia abajo. El truco consiste en rellenar la tabla de DP hacia atrás (desde la esquina inferior derecha hasta la superior izquierda), calculando la salud mínima necesaria en cada celda. En cada celda: dp[i][j] = max(1, min(dp[i+1][j], dp[i][j+1]) - dungeon[i][j]). La salud debe mantenerse siempre al menos en 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

Comparación de problemas de DP en cuadrículas

Los problemas de DP en cuadrículas comparten la misma estructura, pero difieren en la dirección en la que se rellenan y en la operación de transición: Unique Paths utiliza la suma (cuenta todas las formas). Min Path Sum utiliza el mínimo (optimiza). Dungeon Game se rellena hacia atrás (calcula la salud necesaria a partir del futuro). Al enfrentarse a una nueva DP de cuadrícula, pregúntese: (1) ¿Qué representa cada celda? (2) ¿En qué dirección debo rellenar? (3) ¿Qué operación combina las celdas vecinas? Las respuestas a estas tres preguntas revelan la solución 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')

Resumen de complejidad para la DP en cuadrículas

Todos los problemas de DP en cuadrículas de esta lección se ejecutan en un tiempo O(m×n). El espacio va desde O(m×n) para una tabla completa hasta O(n) con un array unidimensional rotativo, y O(1) de espacio auxiliar cuando la cuadrícula se puede modificar in situ. En las entrevistas, mencione la optimización de espacio O(n) después de presentar la solución O(m×n); esto demuestra que conoce las ventajas y desventajas. Para todos los problemas, considere también si existe un atajo greedy, como la fórmula matemática de Unique Paths.

# 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

Comprobación rápida

Ponga a prueba su comprensión de los conceptos de Data Structures & Algorithms — Coding Interview Prep de esta lección.

Resumen de la lección

En esta lección ha aprendido que Unique Paths rellena una tabla bidimensional con dp[i][j] = dp[i-1][j] + dp[i][j-1] y se puede calcular en O(1) mediante combinatoria, que Min Path Sum utiliza la misma estructura, pero sustituye la suma por min para calcular el coste óptimo del camino, y que todos los problemas de DP en cuadrículas comparten el patrón de definir un estado por celda y elegir un operador de transición (suma, mínimo, máximo). A continuación, exploraremos la subsecuencia común más larga mediante DP bidimensional sobre dos secuencias.

Preguntas frecuentes

¿La lección «Rutas únicas y suma mínima de rutas en cuadrículas» es gratis?

Sí — el texto completo de «Rutas únicas y suma mínima de rutas en cuadrículas» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de DSA Interview Prep, actualiza a CoddyKit PRO. El curso de DSA Interview Prep incluye 4 lecciones en total.

¿Qué aprenderé en «Rutas únicas y suma mínima de rutas en cuadrículas»?

Rellene una tabla de DP 2D para rutas únicas con y sin obstáculos y adáptela después para minimizar la suma de valores a lo largo de una ruta. Practicas DSA Interview Prep con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar DSA Interview Prep?

No se requiere experiencia previa. DSA Interview Prep en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 1 de 4.

¿Cuánto tiempo toma la lección «Rutas únicas y suma mínima de rutas en cuadrículas»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de DSA Interview Prep?

Sí. Cada lección de DSA Interview Prep incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. Rutas únicas y suma mínima de rutas en cuadrículas
  2. Subsecuencia común más larga
  3. Distancia de edición (Levenshtein)
  4. Optimización espacial para DP 2D
← Volver a DSA Interview Prep