0Pricing
Coding Interview Prep · Leçon

Chemins uniques et somme minimale des chemins sur des grilles

Remplissez un tableau de DP 2D pour les chemins uniques, avec ou sans obstacles, puis adaptez-le pour minimiser la somme des valeurs le long d’un chemin.

Chemins uniques et somme minimale des chemins sur des grilles est une leçon Coding Interview Prep gratuite sur CoddyKit. Ceci est la leçon 1 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage Coding Interview Prep, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours Coding Interview Prep comprend 4 leçons au total.

Chemins uniques dans une grille

Chemins uniques (LeetCode 62) pose la question suivante : dans une grille m×n, combien de chemins distincts vont du coin supérieur gauche au coin inférieur droit si vous pouvez uniquement vous déplacer vers la droite ou vers le bas ? Pour une grille 3×7, la réponse est 28. L’idée essentielle est que tout chemin vers la cellule (i,j) doit provenir soit de (i-1,j) (au-dessus), soit de (i,j-1) (à gauche), ce qui conduit naturellement à une formulation 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

Tableau DP 2D pour les chemins uniques

Définissez dp[i][j] comme le nombre de chemins vers la cellule (i,j). La première ligne et la première colonne ne contiennent que des 1 (il n’existe qu’une seule façon d’atteindre chaque cellule de la ligne supérieure ou de la colonne la plus à gauche). Pour les autres cellules : dp[i][j] = dp[i-1][j] + dp[i][j-1]. Remplissez le tableau ligne par ligne ; la réponse est dp[m-1][n-1]. Complexité temporelle : O(m×n) ; espace : O(m×n), réductible à 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)

Optimisation de l’espace à O(n)

Puisque dp[i][j] dépend uniquement de la ligne actuelle et de la ligne précédente, vous pouvez remplacer le tableau 2D complet par un seul tableau 1D. Initialisez toutes les valeurs à 1, puis mettez-les à jour sur place pour chaque ligne : dp[j] += dp[j-1]. Après le traitement de la ligne i, dp[j] contient la valeur qui était celle de dp[i][j] dans le tableau 2D. Il s’agit d’un schéma d’optimisation courant pour les problèmes 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

Chemins uniques II : obstacles

Chemins uniques II (LeetCode 63) ajoute des obstacles (cellules marquées par 1) à la grille. Tout chemin traversant un obstacle est invalide ; dp[i][j] = 0 si obstacle[i][j] == 1. Sinon, la récurrence reste la même : dp[i][j] = dp[i-1][j] + dp[i][j-1]. Si le départ ou l’arrivée est bloqué, la réponse est immédiatement 0. Initialisez soigneusement les cas de base : dès qu’un 1 apparaît dans la première ligne ou la première colonne, toutes les cellules suivantes de cette ligne ou colonne valent 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

Problème de la somme minimale d’un chemin

Somme minimale d’un chemin (LeetCode 64) demande, dans une grille m×n remplie d’entiers non négatifs, de trouver le chemin du coin supérieur gauche au coin inférieur droit qui minimise la somme de tous les nombres rencontrés (en se déplaçant uniquement vers la droite ou vers le bas). Par exemple, dans [[1,3,1],[1,5,1],[4,2,1]], le chemin 1→3→1→1→1 donne une somme de 7. L’état DP est le même que pour les chemins uniques, mais la récurrence utilise désormais le minimum au lieu de l’addition.

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)

Implémentation DP de la somme minimale d’un chemin

Définissez dp[i][j] comme le coût minimal pour atteindre la cellule (i,j). Cas de base : dp[0][0] = grid[0][0]. Première ligne : dp[0][j] = dp[0][j-1] + grid[0][j] (le seul chemin vient de la gauche). Première colonne : dp[i][0] = dp[i-1][0] + grid[i][0] (le seul chemin vient du dessus). Cas général : dp[i][j] = grid[i][j] + min(dp[i-1][j], dp[i][j-1]). Il s’agit d’une traduction directe du principe d’optimalité.

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

Somme minimale d’un chemin sur place

Si vous êtes autorisé à modifier la grille d’entrée, vous pouvez la mettre à jour sur place afin d’éviter d’allouer un tableau DP séparé. Cela réduit l’espace auxiliaire à O(1) (en plus de l’entrée). Les examinateurs demandent parfois cette optimisation en entretien : vérifiez d’abord si la modification de l’entrée est autorisée. Dans le cas contraire, l’astuce du tableau glissant 1D offre un espace O(n) sans modifier l’entrée.

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

Somme minimale d’un chemin dans un triangle

Triangle (LeetCode 120) demande la somme minimale d’un chemin du sommet au bas d’un tableau triangulaire, chaque étape menant à un nombre adjacent de la ligne suivante. La DP ascendante est la plus simple : commencez à l’avant-dernière ligne et, pour chaque cellule, ajoutez le minimum des deux cellules qui se trouvent directement en dessous. Cela évite de suivre les indices de départ et fait naturellement remonter la réponse jusqu’au sommet.

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 sur une grille de donjon

Jeu du donjon (LeetCode 174) demande la santé initiale minimale nécessaire pour sauver une princesse dans le coin inférieur droit d’une grille contenant des cellules négatives (dégâts) et positives (soins). Vous devez vous déplacer vers la droite ou vers le bas. L’astuce consiste à remplir le tableau DP à rebours (du coin inférieur droit vers le coin supérieur gauche) en calculant la santé minimale nécessaire dans chaque cellule. Pour chaque cellule : dp[i][j] = max(1, min(dp[i+1][j], dp[i][j+1]) - dungeon[i][j]). La santé doit toujours rester au moins égale à 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

Comparaison des problèmes de DP sur grille

Les problèmes de DP sur grille partagent la même structure, mais diffèrent par le sens de remplissage et l’opération de transition : les chemins uniques utilisent l’addition (compter toutes les possibilités), la somme minimale d’un chemin utilise le minimum (optimiser), et le jeu du donjon se remplit à rebours (santé nécessaire en fonction de la suite du chemin). Face à une nouvelle DP sur grille, posez-vous les questions suivantes : (1) que représente chaque cellule ? (2) dans quel sens dois-je remplir la grille ? (3) quelle opération combine les cellules voisines ? Les réponses à ces trois questions révèlent la solution complète.

# 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')

Résumé de la complexité de la DP sur grille

Tous les problèmes de DP sur grille présentés ici s’exécutent en temps O(m×n). L’espace varie de O(m×n) pour un tableau complet à O(n) avec un tableau glissant 1D, et à O(1) d’espace auxiliaire lorsque la grille peut être modifiée sur place. En entretien, présentez l’optimisation de l’espace en O(n) après la solution en O(m×n) : cela montre que vous connaissez les compromis. Pour tous les problèmes, vérifiez également s’il existe une solution gloutonne plus directe (comme la formule mathématique des chemins uniques).

# 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

Vérification rapide

Vérifiez votre compréhension des concepts de structures de données et d’algorithmes — préparation aux entretiens de programmation — abordés dans cette leçon.

Récapitulatif de la leçon

Dans cette leçon, vous avez appris que : les chemins uniques remplissent un tableau 2D avec dp[i][j] = dp[i-1][j] + dp[i][j-1] et peuvent être calculés en O(1) grâce à la combinatoire ; la somme minimale d’un chemin utilise la même structure, mais remplace l’addition par min pour obtenir le coût optimal du chemin ; et tous les problèmes de DP sur grille suivent le schéma consistant à définir un état par cellule et à choisir un opérateur de transition (somme, min, max). Nous allons maintenant étudier la sous-séquence commune la plus longue à l’aide de la DP 2D appliquée à deux séquences.

Questions Fréquemment Posées

La leçon « Chemins uniques et somme minimale des chemins sur des grilles » est-elle gratuite ?

Oui — le texte complet de « Chemins uniques et somme minimale des chemins sur des grilles » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours Coding Interview Prep, passe à CoddyKit PRO. Le cours Coding Interview Prep comprend 4 leçons au total.

Qu'est-ce que j'apprendrai dans « Chemins uniques et somme minimale des chemins sur des grilles » ?

Remplissez un tableau de DP 2D pour les chemins uniques, avec ou sans obstacles, puis adaptez-le pour minimiser la somme des valeurs le long d’un chemin. Tu pratiques Coding Interview Prep avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.

Dois-je avoir de l'expérience pour commencer Coding Interview Prep ?

Aucune expérience préalable n'est requise. Coding Interview Prep sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 1 sur 4.

Combien de temps prend la leçon « Chemins uniques et somme minimale des chemins sur des grilles » ?

La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.

Peux-tu écrire et exécuter du code dans cette leçon Coding Interview Prep ?

Oui. Chaque leçon Coding Interview Prep inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.

Toutes les leçons de ce cours

  1. Chemins uniques et somme minimale des chemins sur des grilles
  2. Plus longue sous-séquence commune
  3. Distance d’édition (Levenshtein)
  4. Optimisation de l’espace pour la DP 2D
← Retour à Coding Interview Prep