# 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 : ```python 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 : ```python 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 : ```python 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` : ```python 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 : ```python 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 6 - `tableau[3][6] = 15 ≠ tableau[2][6] = 7` → C pris (3kg), w = 6-3 = 3 - `tableau[2][3] = 7 ≠ tableau[1][3] = 2` → B pris (2kg), w = 3-2 = 1 - `tableau[1][1] = 2 ≠ tableau[0][1] = 0` → A pris (1kg), w = 0 Solution : **A + B + C** = 2+5+8 = 15 ✓ **4.** Vérification : ```python 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 : ```python 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 : ```python 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 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.