diff --git a/Programmation_Dynamique/Corrige_TP_Vaccin.md b/Programmation_Dynamique/Corrige_TP_Vaccin.md
new file mode 100644
index 0000000..e1e7b4f
--- /dev/null
+++ b/Programmation_Dynamique/Corrige_TP_Vaccin.md
@@ -0,0 +1,190 @@
+# 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 :
+
+```python
+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 :
+
+```python
+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 :
+
+```python
+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 :
+
+```python
+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)` où `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
+
+
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.
diff --git a/Programmation_Dynamique/EXERCICES.md b/Programmation_Dynamique/EXERCICES.md
index 5185bca..8a20aac 100644
--- a/Programmation_Dynamique/EXERCICES.md
+++ b/Programmation_Dynamique/EXERCICES.md
@@ -22,6 +22,8 @@ Vous souhaitez monter un escalier de `n` marches. À chaque étape, vous pouvez
**6.** Vérifiez que vos deux fonctions donnent les mêmes résultats pour `n` allant de 1 à 10.
+---
+
## Exercice 2 — Rendu de monnaie revisité
On dispose de pièces de valeurs **[2, 5, 10]** (en centimes).
@@ -37,6 +39,8 @@ On dispose de pièces de valeurs **[2, 5, 10]** (en centimes).
**4.** Modifiez la fonction pour qu'elle retourne non seulement le nombre de pièces, mais aussi la **liste des pièces utilisées** (comme la reconstruction dans le sac à dos).
+---
+
## Exercice 3 — Sac à dos : à la main et en code
On dispose des objets suivants, avec un sac de capacité **6 kg** :
@@ -56,6 +60,8 @@ On dispose des objets suivants, avec un sac de capacité **6 kg** :
**4.** Vérifiez vos réponses en exécutant `sac_reconstruction(objets, 6)`.
+---
+
## Exercice 4 — Triangle de Pascal ★
Le **triangle de Pascal** est construit selon la règle suivante :
diff --git a/Programmation_Dynamique/README.md b/Programmation_Dynamique/README.md
index 6d66497..8ce3291 100644
--- a/Programmation_Dynamique/README.md
+++ b/Programmation_Dynamique/README.md
@@ -6,7 +6,7 @@

-> La programmation dynamique est une méthode algorithmique permettant de résoudre efficacement des problèmes d'optimisation en mémorisant les résultats des sous-problèmes déjà résolus.
+> La programmation dynamique est une méthode algorithmique permettant de résoudre efficacement des problèmes complexes en mémorisant les résultats des sous-problèmes déjà résolus.
---
diff --git a/Programmation_Dynamique/RENDU_MONNAIE.md b/Programmation_Dynamique/RENDU_MONNAIE.md
index f9a813f..e1f09ee 100644
--- a/Programmation_Dynamique/RENDU_MONNAIE.md
+++ b/Programmation_Dynamique/RENDU_MONNAIE.md
@@ -99,7 +99,7 @@ Chaque valeur de `rendu(s)` n'est calculée qu'une seule fois et mémorisée dan
### Version Bottom-Up (tableau)
-On remplit un tableau `t` où `t[s]` représente le nombre minimum de pièces pour rendre la somme `s`, en partant de `s = 0` jusqu'à `s = somme`.
+On remplit un tableau `tableau` où `tableau[s]` représente le nombre minimum de pièces pour rendre la somme `s`, en partant de `s = 0` jusqu'à `s = somme`.
```python
def rendu_bottom_up(pieces, somme):
diff --git a/Programmation_Dynamique/SAC_A_DOS.md b/Programmation_Dynamique/SAC_A_DOS.md
index abe4125..b635160 100644
--- a/Programmation_Dynamique/SAC_A_DOS.md
+++ b/Programmation_Dynamique/SAC_A_DOS.md
@@ -40,7 +40,7 @@ Optimal : Nourriture (3kg, val 7) + Trousse (2kg, val 6) = 5kg → **valeur = 13
Notons `sac(i, w)` la valeur maximale qu'on peut atteindre en choisissant parmi les `i` premiers objets avec une capacité restante de `w`.
-- **Cas de base :** `sac(0, w) = 0` (aucun objet disponible) et `sac(i, 0) = 0` (sac plein)
+- **Cas de base :** `sac(0, w) = 0` (aucun objet disponible) et `sac(i, 0) = 0` (capacité épuisée)
- **Cas récursif :** Pour l'objet `i` de poids `p_i` et de valeur `v_i` :
- Si `p_i > w` : on ne peut pas le prendre → `sac(i, w) = sac(i-1, w)`
- Sinon, on choisit le meilleur entre le prendre et ne pas le prendre :
diff --git a/Programmation_Dynamique/TP_Donjon.md b/Programmation_Dynamique/TP_Donjon.md
index 2dc9af0..712f94b 100644
--- a/Programmation_Dynamique/TP_Donjon.md
+++ b/Programmation_Dynamique/TP_Donjon.md
@@ -36,7 +36,7 @@ Est-ce le meilleur chemin ? C'est ce que vous allez découvrir.
## Partie 2 — Formulation récursive
-Notons `donjon(i, j)` l'or maximum qu'on peut ramasser en partant de `(i, j)` pour atteindre `(n-1, m-1)`.
+Notons `donjon(i, j)` l'or maximum qu'on peut ramasser depuis `(0, 0)` jusqu'à `(i, j)`.
**Question 4.** Quels sont les cas de base de cette fonction récursive ? *(Pensez aux bords de la grille.)*
@@ -84,8 +84,6 @@ def donjon_memo(grille):
**Question 10.** Remplissez à la main le tableau `t[i][j]` représentant l'or maximum qu'on peut ramasser **depuis `(0,0)` jusqu'à `(i,j)`** pour la grille de l'exemple.
-*(Attention : ici on part de `(0,0)` et on avance, pas de `(i,j)` vers la sortie.)*
-
**Question 11.** Implémentez `donjon_bottom_up(grille)` :
```python
diff --git a/Programmation_Dynamique/TP_Vaccin.md b/Programmation_Dynamique/TP_Vaccin.md
new file mode 100644
index 0000000..f838324
--- /dev/null
+++ b/Programmation_Dynamique/TP_Vaccin.md
@@ -0,0 +1,159 @@
+# TP — L'Algorithme du Vaccin
+
+## Contexte
+
+Vous êtes ingénieur·e en bio-informatique dans un laboratoire pharmaceutique. Une nouvelle épidémie virale se propage, et votre équipe dispose d'un **bioréacteur** capable de produire des anticorps pendant un temps limité.
+
+Vous avez identifié **n variants du virus**, chacun nécessitant :
+- un **temps de production** (en heures) pour fabriquer les anticorps correspondants
+- une **efficacité estimée** (score de 1 à 100) contre la souche circulante
+
+Le bioréacteur ne peut fonctionner que pendant **T heures** au total. Vous devez choisir quels anticorps produire pour **maximiser l'efficacité totale** du vaccin, sans dépasser le temps disponible.
+
+> Ce problème est directement modélisé par le **problème du sac à dos 0/1** : chaque anticorps est produit entièrement ou pas du tout.
+
+**Exemple de données :**
+
+| Anticorps | Temps (h) | Efficacité | Ratio eff/temps |
+|-----------|-----------|------------|-----------------|
+| Alpha | 3 h | 40 | 13,3 |
+| Bêta | 5 h | 70 | 14,0 |
+| Gamma | 2 h | 30 | **15,0** ← meilleur |
+| Delta | 7 h | 100 | 14,3 |
+| Epsilon | 6 h | 85 | 14,2 |
+
+Temps disponible : **10 heures**
+
+---
+
+## Partie 1 — Comprendre le problème
+
+**Question 1.** En utilisant l'algorithme glouton (trier par ratio efficacité/temps décroissant), quels anticorps choisissez-vous ? Quelle est l'efficacité totale obtenue ?
+
+**Question 2.** Essayez d'autres combinaisons à la main. Trouvez-vous une solution d'efficacité strictement supérieure à celle du glouton ?
+
+**Question 3.** Combien y a-t-il de combinaisons possibles en tout pour `n = 5` anticorps ? Pour `n = 30` ? Qu'en déduisez-vous sur une approche exhaustive ?
+
+---
+
+## Partie 2 — Formulation récursive
+
+Notons `vaccin(i, t)` l'efficacité maximale qu'on peut atteindre en choisissant parmi les `i` premiers anticorps avec un temps restant de `t` heures.
+
+**Question 4.** Quels sont les cas de base de cette fonction récursive ?
+
+**Question 5.** Écrivez la relation de récurrence pour `vaccin(i, t)` sachant que l'anticorps `i` a un temps de production `duree_i` et une efficacité `eff_i`.
+
+**Question 6.** Implémentez la version naïve `vaccin_naif(anticorps, i, t)` :
+
+```python
+def vaccin_naif(anticorps, i, t):
+ # anticorps : liste de tuples (duree, efficacite)
+ # À compléter
+ pass
+
+# Test
+anticorps = [(3, 40), (5, 70), (2, 30), (7, 100), (6, 85)]
+n = len(anticorps)
+print(vaccin_naif(anticorps, n, 10)) # doit afficher 140
+```
+
+---
+
+## Partie 3 — Version Top-Down (mémoïsation)
+
+**Question 7.** Pourquoi la version naïve recalcule-t-elle des sous-problèmes ? Donnez un exemple de sous-problème qui serait recalculé plusieurs fois.
+
+**Question 8.** Implémentez `vaccin_memo(anticorps, temps_total)` avec mémoïsation :
+
+```python
+def vaccin_memo(anticorps, temps_total):
+ memo = {}
+
+ def vaccin(i, t):
+ # À compléter
+ pass
+
+ return vaccin(len(anticorps), temps_total)
+```
+
+**Question 9.** Vérifiez que vous obtenez le même résultat qu'à la Question 6.
+
+---
+
+## Partie 4 — Version Bottom-Up (tableau)
+
+**Question 10.** Remplissez à la main le tableau `tableau[i][t]` pour les **3 premiers anticorps** (Alpha, Bêta, Gamma) avec un temps total de **6 heures** :
+
+| | t=0 | t=1 | t=2 | t=3 | t=4 | t=5 | t=6 |
+|---|---|---|---|---|---|---|---|
+| i=0 (aucun) | | | | | | | |
+| i=1 (Alpha 3h, 40) | | | | | | | |
+| i=2 (+Bêta 5h, 70) | | | | | | | |
+| i=3 (+Gamma 2h, 30) | | | | | | | |
+
+**Question 11.** Implémentez `vaccin_bottom_up(anticorps, temps_total)` :
+
+```python
+def vaccin_bottom_up(anticorps, temps_total):
+ n = len(anticorps)
+ # tableau[i][t] = efficacité max avec les i premiers anticorps, temps t disponible
+ 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):
+ # À compléter
+ pass
+
+ return tableau[n][temps_total]
+```
+
+**Question 12.** Vérifiez que vous obtenez 140 pour l'exemple complet (5 anticorps, 10 heures).
+
+---
+
+## Partie 5 — Reconstruction de la solution
+
+**Question 13.** Modifiez `vaccin_bottom_up` pour qu'elle retourne non seulement l'efficacité maximale, mais aussi la **liste des anticorps sélectionnés** :
+
+```python
+def vaccin_reconstruction(anticorps, temps_total):
+ """
+ Retourne (efficacite_max, liste_anticorps_choisis)
+ """
+ n = len(anticorps)
+ tableau = [[0] * (temps_total + 1) for _ in range(n + 1)]
+
+ # Remplissage du tableau (identique à vaccin_bottom_up)
+ # À compléter
+
+ # Reconstruction : on remonte le tableau
+ choisis = []
+ t = temps_total
+ for i in range(n, 0, -1):
+ # À compléter
+ pass
+
+ return tableau[n][temps_total], choisis
+```
+
+**Question 14.** Testez votre fonction. Vérifiez que les anticorps sélectionnés ont bien une durée totale ≤ 10 heures et une efficacité totale de 140.
+
+**Question 15.** Quelle est la complexité en temps et en espace de la version bottom-up ? Exprimez-la en fonction de `n` (nombre d'anticorps) et `T` (temps total disponible).
+
+---
+
+## Partie 6 — Bonus : contraintes supplémentaires
+
+**Question 16.** *(Bonus)* En réalité, certains anticorps sont **incompatibles** entre eux (ils réagissent chimiquement). Supposez que les anticorps Alpha et Gamma ne peuvent pas être produits ensemble.
+
+Comment modifieriez-vous l'algorithme pour prendre en compte cette contrainte ? Proposez une approche (pas nécessairement de code).
+
+---
+
+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.