# Corrigé — TP Le Donjon du Dragon ## Partie 1 — Comprendre le problème **Question 1.** Chemins de `(0,0)` à `(2,3)` (grille 3×4) en allant uniquement droite/bas : Il faut faire exactement 3 pas vers la droite et 2 pas vers le bas, soit C(5,2) = **10 chemins**. **Question 2.** Le chemin optimal est `(0,0)→(1,0)→(1,1)→(2,1)→(2,2)→(2,3)` : 3+5+9+2+8-5 = **22** d'or. | Chemin | Or total | |--------|----------| | Tout droite puis tout bas | 3-1+4+1+6-5 = **8** | | Bas, droite×3, bas | 3+5+9-2+6-5 = **16** | | Bas×2, droite×3 | 3+5-3+2+8-5 = **10** | | Bas, droite, bas, droite×2 | 3+5+9+2+8-5 = **22** ← optimal | **Question 3.** Pour une grille `n×m`, le nombre de chemins est C(n+m-2, n-1) (combinaison : choisir quand descendre parmi tous les pas). ## Partie 2 — Formulation récursive **Question 4.** Cas de base : - `donjon(i, 0)` : on ne peut aller que vers le bas → somme de la colonne 0 jusqu'à `i` - `donjon(0, j)` : on ne peut aller que vers la droite → somme de la ligne 0 jusqu'à `j` **Question 5.** Relation générale : ``` donjon(i, j) = grille[i][j] + max(donjon(i-1, j), donjon(i, j-1)) ``` *(On arrive en `(i,j)` depuis le haut ou depuis la gauche, on choisit le meilleur.)* **Question 6.** Version naïve : ```python def donjon_naif(grille, i, j): if i == 0 and j == 0: return grille[0][0] if i == 0: return grille[0][j] + donjon_naif(grille, 0, j - 1) if j == 0: return grille[i][0] + donjon_naif(grille, i - 1, 0) return grille[i][j] + max(donjon_naif(grille, i - 1, j), donjon_naif(grille, i, j - 1)) grille = [ [ 3, -1, 4, 1], [ 5, 9, -2, 6], [-3, 2, 8, -5] ] n, m = len(grille), len(grille[0]) print(donjon_naif(grille, n - 1, m - 1)) # 22 ``` ## Partie 3 — Version Top-Down (mémoïsation) **Question 7.** Oui, beaucoup de sous-problèmes sont recalculés. Par exemple, `donjon(1,1)` est appelé depuis `donjon(2,1)` et depuis `donjon(1,2)`. ```python compteur = 0 def donjon_naif_compte(grille, i, j): global compteur compteur += 1 if i == 0 and j == 0: return grille[0][0] if i == 0: return grille[0][j] + donjon_naif_compte(grille, 0, j - 1) if j == 0: return grille[i][0] + donjon_naif_compte(grille, i - 1, 0) return grille[i][j] + max(donjon_naif_compte(grille, i - 1, j), donjon_naif_compte(grille, i, j - 1)) donjon_naif_compte(grille, 2, 3) print(f"Nombre d'appels : {compteur}") # bien plus que les 12 cases de la grille ``` **Question 8.** Version mémoïsation : ```python def donjon_memo(grille): n = len(grille) m = len(grille[0]) memo = {} def donjon(i, j): if (i, j) in memo: return memo[(i, j)] if i == 0 and j == 0: resultat = grille[0][0] elif i == 0: resultat = grille[0][j] + donjon(0, j - 1) elif j == 0: resultat = grille[i][0] + donjon(i - 1, 0) else: resultat = grille[i][j] + max(donjon(i - 1, j), donjon(i, j - 1)) memo[(i, j)] = resultat return resultat return donjon(n - 1, m - 1) print(donjon_memo(grille)) # 22 ``` **Question 9.** Les deux versions donnent 22 ✓ ## Partie 4 — Version Bottom-Up (tableau) **Question 10.** Tableau `t[i][j]` = or max depuis `(0,0)` jusqu'à `(i,j)` : ``` grille : tableau : [ 3, -1, 4, 1] [ 3, 2, 6, 7] [ 5, 9, -2, 6] [ 8, 17, 15, 21] [-3, 2, 8, -5] [ 5, 19, 27, 22] ``` **Question 11.** Implémentation : ```python def donjon_bottom_up(grille): n = len(grille) m = len(grille[0]) tableau = [[0] * m for _ in range(n)] tableau[0][0] = grille[0][0] for j in range(1, m): # première ligne tableau[0][j] = tableau[0][j - 1] + grille[0][j] for i in range(1, n): # première colonne tableau[i][0] = tableau[i - 1][0] + grille[i][0] for i in range(1, n): # reste du tableau for j in range(1, m): tableau[i][j] = grille[i][j] + max(tableau[i - 1][j], tableau[i][j - 1]) return tableau[n - 1][m - 1] print(donjon_bottom_up(grille)) # 22 ``` **Question 12.** Les trois versions donnent 22 ✓ ## Partie 5 — Bonus : retrouver le chemin optimal **Question 13.** Reconstruction du chemin : ```python def donjon_chemin(grille): n = len(grille) m = len(grille[0]) tableau = [[0] * m for _ in range(n)] tableau[0][0] = grille[0][0] for j in range(1, m): tableau[0][j] = tableau[0][j - 1] + grille[0][j] for i in range(1, n): tableau[i][0] = tableau[i - 1][0] + grille[i][0] for i in range(1, n): for j in range(1, m): tableau[i][j] = grille[i][j] + max(tableau[i - 1][j], tableau[i][j - 1]) # Reconstruction : on remonte depuis (n-1, m-1) chemin = [] i, j = n - 1, m - 1 while i > 0 or j > 0: chemin.append((i, j)) if i == 0: j -= 1 elif j == 0: i -= 1 elif tableau[i - 1][j] > tableau[i][j - 1]: i -= 1 else: j -= 1 chemin.append((0, 0)) chemin.reverse() return tableau[n - 1][m - 1], chemin valeur, chemin = donjon_chemin(grille) print(f"Or maximal : {valeur}") # 22 print(f"Chemin optimal : {chemin}") # [(0,0), (1,0), (1,1), (2,1), (2,2), (2,3)] ``` --- Auteur : Florian Mathieu Licence CC BY NC Licence Creative Commons
Ce cours est mis à disposition selon les termes de la Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Partage dans les Mêmes Conditions 4.0 International.