6.0 KiB
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'àidonjon(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 :
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).
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 :
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 :
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 :
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
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.