Files
TermNSI/Programmation_Dynamique/Corrige_TP_Vaccin.md

6.4 KiB
Raw Blame History

Corrigé — TP L'Algorithme du Vaccin

Partie 1 — Comprendre le problème

Question 1. Algorithme glouton (tri par ratio efficacité/temps décroissant) :

Ratios : Gamma (15,0), Delta (14,3), Epsilon (14,2), Bêta (14,0), Alpha (13,3)

  • Prendre Gamma : 2 h utilisées, efficacité 30, reste 8 h
  • Prendre Delta : 2+7=9 h utilisées, efficacité 130, reste 1 h
  • Plus aucun anticorps ne tient en 1 h → arrêt

Résultat glouton : 130 (Gamma + Delta)

Question 2. Oui : Alpha + Bêta + Gamma = 3+5+2 = 10 h, efficacité 40+70+30 = 140. Le glouton a raté la solution optimale.

Question 3. Pour 5 anticorps : 2⁵ = 32 combinaisons. Pour 30 anticorps : 2³⁰ ≈ 1 milliard. L'approche exhaustive devient impraticable très rapidement.


Partie 2 — Formulation récursive

Question 4. Cas de base :

  • vaccin(0, t) = 0 (aucun anticorps disponible)
  • vaccin(i, 0) = 0 (temps épuisé)

Question 5. Relation de récurrence :

vaccin(i, t) = vaccin(i-1, t)                          si duree_i > t
vaccin(i, t) = max(vaccin(i-1, t),                     sinon
                   eff_i + vaccin(i-1, t - duree_i))

Question 6. Version naïve :

def vaccin_naif(anticorps, i, t):
    if i == 0 or t == 0:
        return 0
    duree, eff = anticorps[i - 1]
    if duree > t:
        return vaccin_naif(anticorps, i - 1, t)
    else:
        sans = vaccin_naif(anticorps, i - 1, t)
        avec = eff + vaccin_naif(anticorps, i - 1, t - duree)
        return max(sans, avec)

anticorps = [(3, 40), (5, 70), (2, 30), (7, 100), (6, 85)]
n = len(anticorps)
print(vaccin_naif(anticorps, n, 10))   # 140

Partie 3 — Version Top-Down (mémoïsation)

Question 7. Oui. Par exemple, vaccin(2, 5) (2 premiers anticorps, 5 h restantes) peut être appelé en ne prenant pas Alpha, mais aussi après avoir pris Alpha (si on avait commencé avec 8 h). Sans mémoïsation, ce sous-problème est recalculé à chaque fois.

Question 8. Version mémoïsation :

def vaccin_memo(anticorps, temps_total):
    memo = {}

    def vaccin(i, t):
        if i == 0 or t == 0:
            return 0
        if (i, t) in memo:
            return memo[(i, t)]
        duree, eff = anticorps[i - 1]
        if duree > t:
            resultat = vaccin(i - 1, t)
        else:
            sans = vaccin(i - 1, t)
            avec = eff + vaccin(i - 1, t - duree)
            resultat = max(sans, avec)
        memo[(i, t)] = resultat
        return resultat

    return vaccin(len(anticorps), temps_total)

print(vaccin_memo(anticorps, 10))   # 140

Question 9. Les deux versions donnent 140 ✓


Partie 4 — Version Bottom-Up (tableau)

Question 10. Tableau pour Alpha (3h,40), Bêta (5h,70), Gamma (2h,30), T=6 :

t=0 t=1 t=2 t=3 t=4 t=5 t=6
i=0 (aucun) 0 0 0 0 0 0 0
i=1 (Alpha 3h, 40) 0 0 0 40 40 40 40
i=2 (+Bêta 5h, 70) 0 0 0 40 40 70 70
i=3 (+Gamma 2h, 30) 0 0 30 40 40 70 70

Avec 6 h, le meilleur est Bêta seul (70), ou Gamma+Alpha ne tient pas car 2+3=5 h → efficacité 70 aussi.

Question 11. Implémentation :

def vaccin_bottom_up(anticorps, temps_total):
    n = len(anticorps)
    tableau = [[0] * (temps_total + 1) for _ in range(n + 1)]

    for i in range(1, n + 1):
        duree, eff = anticorps[i - 1]
        for t in range(temps_total + 1):
            if duree > t:
                tableau[i][t] = tableau[i - 1][t]
            else:
                tableau[i][t] = max(
                    tableau[i - 1][t],
                    eff + tableau[i - 1][t - duree]
                )

    return tableau[n][temps_total]

print(vaccin_bottom_up(anticorps, 10))   # 140

Question 12. Les trois versions donnent 140 ✓


Partie 5 — Reconstruction de la solution

Question 13. Reconstruction :

def vaccin_reconstruction(anticorps, temps_total):
    n = len(anticorps)
    tableau = [[0] * (temps_total + 1) for _ in range(n + 1)]

    for i in range(1, n + 1):
        duree, eff = anticorps[i - 1]
        for t in range(temps_total + 1):
            if duree > t:
                tableau[i][t] = tableau[i - 1][t]
            else:
                tableau[i][t] = max(
                    tableau[i - 1][t],
                    eff + tableau[i - 1][t - duree]
                )

    # Reconstruction : on remonte le tableau
    choisis = []
    t = temps_total
    for i in range(n, 0, -1):
        if tableau[i][t] != tableau[i - 1][t]:
            choisis.append(anticorps[i - 1])
            t -= anticorps[i - 1][0]

    return tableau[n][temps_total], choisis


efficacite, choix = vaccin_reconstruction(anticorps, 10)
print(f"Efficacité maximale : {efficacite}")   # 140
print(f"Anticorps choisis : {choix}")          # [(3,40), (5,70), (2,30)] → Alpha + Bêta + Gamma

Question 14. Vérification :

  • Alpha (3h) + Bêta (5h) + Gamma (2h) = 10 h ≤ 10 h ✓
  • 40 + 70 + 30 = 140

Question 15. Complexité :

  • Temps : O(n × T) — on remplit un tableau de n+1 lignes et T+1 colonnes
  • Espace : O(n × T) — pour stocker le tableau

Avec n = nombre d'anticorps et T = temps total disponible.


Partie 6 — Bonus : contraintes supplémentaires

Question 16. Pour gérer l'incompatibilité Alpha/Gamma, on peut ajouter une dimension à l'état : au lieu de vaccin(i, t), on utilise vaccin(i, t, alpha_pris)alpha_pris vaut 1 si Alpha a déjà été sélectionné, 0 sinon. Si alpha_pris == 1 et que l'anticorps courant est Gamma (ou vice-versa), on interdit sa sélection.

Cette approche généralise naturellement : chaque contrainte d'incompatibilité entre deux objets peut être encodée comme une variable booléenne supplémentaire dans l'état. La complexité devient O(n × T × 2^k) où k est le nombre de contraintes d'incompatibilité.


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.