Files
TermNSI/Programmation_Dynamique/Corrige_Exercices.md

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 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 :

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

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.