5.3 KiB
Corrigé — Exercices Programmation Dynamique
Exercice 1 — L'escalier
1. Pour n = 5 : escalier(5) = escalier(4) + escalier(3) = 5 + 3 = 8 façons
2. La suite escalier(n) suit la suite de Fibonacci (décalée d'un rang) : 1, 2, 3, 5, 8, 13...
3. Relation de récurrence :
escalier(1) = 1
escalier(2) = 2
escalier(n) = escalier(n-1) + escalier(n-2) pour n > 2
4. Version mémoïsation :
def escalier_memo(n):
memo = {1: 1, 2: 2}
def escalier(k):
if k in memo:
return memo[k]
memo[k] = escalier(k - 1) + escalier(k - 2)
return memo[k]
return escalier(n)
5. Version bottom-up :
def escalier_bottom_up(n):
if n == 1:
return 1
tableau = [0] * (n + 1)
tableau[1] = 1
tableau[2] = 2
for i in range(3, n + 1):
tableau[i] = tableau[i - 1] + tableau[i - 2]
return tableau[n]
6. Vérification :
for i in range(1, 11):
assert escalier_memo(i) == escalier_bottom_up(i), f"Erreur pour n={i}"
print("Toutes les valeurs correspondent !")
# Résultats : 1, 2, 3, 5, 8, 13, 21, 34, 55, 89
Exercice 2 — Rendu de monnaie revisité
1. Avec les pièces [2, 5, 10] et somme = 6 : le glouton prend 5 (la plus grande pièce ≤ 6), il reste 1 à rendre, mais aucune pièce de [2, 5, 10] ne permet de rendre 1. Le glouton échoue.
2. Oui. La programmation dynamique explore toutes les combinaisons et trouve : 2+2+2 = 3 pièces. La somme 6 est bien rendable, le glouton était simplement mal parti en choisissant 5 en premier.
3. rendu_bottom_up :
def rendu_bottom_up(pieces, somme):
tableau = [float('inf')] * (somme + 1)
tableau[0] = 0
for s in range(1, somme + 1):
for piece in pieces:
if piece <= s and tableau[s - piece] + 1 < tableau[s]:
tableau[s] = tableau[s - piece] + 1
return tableau[somme]
# Tests
print(rendu_bottom_up([2, 5, 10], 6)) # 3 (2+2+2)
print(rendu_bottom_up([1, 3, 4], 6)) # 2 (3+3)
print(rendu_bottom_up([1, 5, 6, 9], 11)) # 2 (5+6)
4. Version avec reconstruction :
def rendu_avec_pieces(pieces, somme):
tableau = [float('inf')] * (somme + 1)
tableau[0] = 0
derniere_piece = [-1] * (somme + 1) # mémorise quelle pièce a été utilisée
for s in range(1, somme + 1):
for piece in pieces:
if piece <= s and tableau[s - piece] + 1 < tableau[s]:
tableau[s] = tableau[s - piece] + 1
derniere_piece[s] = piece
# Reconstruction
pieces_utilisees = []
s = somme
while s > 0:
p = derniere_piece[s]
pieces_utilisees.append(p)
s -= p
return tableau[somme], pieces_utilisees
print(rendu_avec_pieces([1, 3, 4], 6))
# (2, [3, 3])
Exercice 3 — Sac à dos : à la main et en code
1. Tableau tableau[i][w] :
objets = [(1,2), (2,5), (3,8), (4,9)], capacité = 6
| w=0 | w=1 | w=2 | w=3 | w=4 | w=5 | w=6 | |
|---|---|---|---|---|---|---|---|
| i=0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| i=1 (A:1kg,2) | 0 | 2 | 2 | 2 | 2 | 2 | 2 |
| i=2 (+B:2kg,5) | 0 | 2 | 5 | 7 | 7 | 7 | 7 |
| i=3 (+C:3kg,8) | 0 | 2 | 5 | 8 | 10 | 13 | 15 |
| i=4 (+D:4kg,9) | 0 | 2 | 5 | 8 | 10 | 13 | 15 |
2. Valeur maximale : 15
3. Reconstruction depuis tableau[4][6] = 15 :
tableau[4][6] = tableau[3][6]→ D non pris, w reste 6tableau[3][6] = 15 ≠ tableau[2][6] = 7→ C pris (3kg), w = 6-3 = 3tableau[2][3] = 7 ≠ tableau[1][3] = 2→ B pris (2kg), w = 3-2 = 1tableau[1][1] = 2 ≠ tableau[0][1] = 0→ A pris (1kg), w = 0
Solution : A + B + C = 2+5+8 = 15 ✓
4. Vérification :
objets = [(1, 2), (2, 5), (3, 8), (4, 9)]
valeur, choix = sac_reconstruction(objets, 6)
print(valeur, choix) # 15, [(3, 8), (2, 5), (1, 2)]
Exercice 4 — Triangle de Pascal
1. Relation de récurrence :
pascal(0, 0) = 1
pascal(n, 0) = 1 ← bord gauche
pascal(n, n) = 1 ← bord droit
pascal(n, k) = pascal(n-1, k-1) + pascal(n-1, k) pour 0 < k < n
2. Implémentation bottom-up :
def triangle_pascal(n):
triangle = []
for ligne in range(n):
t = [1] * (ligne + 1) # initialise avec des 1
for k in range(1, ligne): # cases intérieures
t[k] = triangle[ligne-1][k-1] + triangle[ligne-1][k]
triangle.append(t)
return triangle
3. Vérification :
resultat = triangle_pascal(5)
assert resultat == [[1], [1, 1], [1, 2, 1], [1, 3, 3, 1], [1, 4, 6, 4, 1]]
print("Correct !")
4. Bonus : La somme des éléments de la ligne n est 2ⁿ. Par exemple, ligne 4 : 1+4+6+4+1 = 16 = 2⁴. Lien avec Fibonacci : la somme des diagonales "montantes" du triangle de Pascal donne les nombres de Fibonacci.
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.