<?xml version="1.0" encoding="UTF-8"?>
<quiz>
<question type="category">
  <category>
    <text>$course$/QCM de NSI/Terminale/Programmation dynamique</text>
  </category>
  <info format="html">
    <text><![CDATA[<p>Sous-problèmes qui se chevauchent, sous-structure optimale,<br/>
mémoïsation descendante, tabulation ascendante,<br/>
exemples classiques (suite de Fibonacci, plus longue<br/>
sous-séquence commune, sac à dos, distance d'édition,<br/>
rendu de monnaie).</p>]]></text>
  </info>
</question>

<question type="multichoice">
  <name>
    <text>Programmation dynamique — Q01 : Idée centrale de la programmation dynamique</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>Quel est le principe fondamental de la<br/>
programmation dynamique ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>Le terme « programmation » dans « programmation<br/>
dynamique » vient de l'optimisation linéaire<br/>
(Bellman, années $1950$), et non de la<br/>
programmation informatique. Il signifie « plan,<br/>
tableau », ce qui justifie la notion de<br/>
tabulation.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Diviser le problème en deux moitiés et résoudre chacune indépendamment</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Cette description correspond au paradigme<br/>
« diviser pour régner ». La différence<br/>
essentielle est que les sous-problèmes y sont<br/>
<strong>disjoints</strong>, alors qu'en programmation<br/>
dynamique ils se <strong>recouvrent</strong>.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Faire à chaque étape le choix qui semble localement le meilleur</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Cette description correspond au principe des<br/>
algorithmes gloutons. La programmation<br/>
dynamique, au contraire, explore toutes les<br/>
possibilités de manière organisée, en<br/>
mémorisant les solutions des sous-problèmes.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Modifier le programme à mesure qu'il s'exécute</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Cette description évoque l'auto-modification<br/>
de code, qui n'a aucun rapport avec la<br/>
programmation dynamique. Le mot<br/>
« dynamique » désigne ici le caractère<br/>
temporel de la décision, pas une<br/>
modification du programme.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>Mémoriser les solutions des sous-problèmes</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>La programmation dynamique exploite la<br/>
présence de sous-problèmes qui se répètent<br/>
dans la décomposition récursive. En les<br/>
mémorisant, on évite la croissance<br/>
exponentielle du nombre de recalculs.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Programmation dynamique — Q02 : Mémoïsation</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>Qu'est-ce que la <strong>mémoïsation</strong> ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>Le mot « mémoïsation » associe les idées de<br/>
mémoire et de récursivité. C'est l'approche<br/>
« descendante » de la programmation dynamique :<br/>
on part du problème complet, on descend dans les<br/>
sous-problèmes en mémorisant les résultats au<br/>
passage.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>La comparaison de deux algorithmes pour choisir le plus rapide</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Cette opération relève de la mesure de<br/>
performance, et n'a aucun rapport avec la<br/>
mémoïsation.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>La conversion automatique d'une fonction récursive en boucle itérative</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Cette transformation s'appelle la<br/>
<strong>dérécursivation</strong>, et c'est une technique<br/>
différente. La mémoïsation conserve la<br/>
structure récursive de la fonction, en y<br/>
ajoutant simplement un cache.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>La compression d'un programme en mémoire pour gagner de la place</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>La mémoïsation n'a aucun rapport avec la<br/>
taille du programme. Elle concerne le<br/>
<strong>temps de calcul</strong>, en évitant de<br/>
recalculer plusieurs fois la même valeur.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>Stocker les résultats déjà calculés dans un cache</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>À chaque appel récursif, l'algorithme<br/>
vérifie si le résultat est déjà connu<br/>
(typiquement dans un dictionnaire ou un<br/>
tableau) avant de le calculer. Si la valeur<br/>
est déjà disponible, elle est renvoyée<br/>
directement.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Programmation dynamique — Q03 : Tabulation</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>Qu'appelle-t-on <strong>tabulation</strong> en programmation<br/>
dynamique ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>La mémoïsation et la tabulation sont les deux<br/>
faces de la programmation dynamique : la<br/>
mémoïsation est descendante (récursive, avec un<br/>
cache) ; la tabulation est ascendante<br/>
(itérative, avec un tableau). En pratique, la<br/>
tabulation est souvent plus rapide, car elle<br/>
évite le surcoût des appels récursifs.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Le découpage d'un problème en sous-problèmes égaux</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Cette description correspond au paradigme<br/>
« diviser pour régner ». La tabulation<br/>
consiste à remplir un tableau ; elle ne<br/>
découpe pas le problème.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>La création d'une table HTML pour afficher les résultats</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>La « tabulation » dans ce contexte<br/>
algorithmique n'a aucun rapport avec une<br/>
mise en forme HTML. Il s'agit du<br/>
remplissage progressif d'un tableau de<br/>
valeurs.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Une optimisation des comparaisons utilisée dans un algorithme de tri</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>La tabulation est spécifique à la<br/>
programmation dynamique. Elle n'a pas de<br/>
rôle particulier dans les algorithmes de<br/>
tri.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>Remplir un tableau des petits aux grands sous-problèmes</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>C'est l'approche dite « ascendante » : on<br/>
part des cas de base et on remplit<br/>
progressivement un tableau, sans aucune<br/>
récursion.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Programmation dynamique — Q04 : Suite de Fibonacci en programmation dynamique</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>Quelle est la complexité de la suite de Fibonacci<br/>
calculée avec mémoïsation ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>Le passage de $O(2^n)$ à $O(n)$ pour la suite<br/>
de Fibonacci est l'exemple canonique de la<br/>
puissance de la programmation dynamique. Pour<br/>
$n = 50$, on passe ainsi de plus d'un million<br/>
d'appels à seulement $51$.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>$O(n^2)$</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>La programmation dynamique appliquée à<br/>
Fibonacci ne nécessite qu'un parcours<br/>
linéaire des valeurs, et non une double<br/>
itération qui justifierait une complexité<br/>
quadratique.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>$O(\log n)$</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Avec la récurrence $f(n) = f(n-1) + f(n-2)$,<br/>
la mémoïsation calcule une fois chaque<br/>
valeur de $f(0)$ à $f(n)$ : le nombre de<br/>
calculs est proportionnel à $n$, et non à<br/>
$\log n$.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>$O(2^n)$</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Cette complexité correspond à la version<br/>
<strong>récursive naïve</strong>, sans mémoïsation. Le<br/>
recours à un cache réduit drastiquement ce<br/>
coût.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>$O(n)$</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Avec mémoïsation, chaque valeur<br/>
$\text{fib}(k)$ pour $k$ allant de $0$ à<br/>
$n$ n'est calculée qu'une seule fois, ce<br/>
qui donne $n$ calculs au total.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Programmation dynamique — Q05 : Sous-problèmes qui se chevauchent</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>Que signifie « le problème a des sous-problèmes<br/>
<strong>qui se chevauchent</strong> » ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>Cette propriété est essentielle pour que la<br/>
programmation dynamique soit utile. Si les<br/>
sous-problèmes sont uniques, la mémoïsation<br/>
n'apporte rien et on retombe sur le paradigme<br/>
« diviser pour régner » classique.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Les sous-problèmes ont tous la même solution</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Les sous-problèmes peuvent avoir des<br/>
solutions très différentes, ce qui est<br/>
d'ailleurs le cas le plus courant. Le<br/>
chevauchement signifie qu'ils<br/>
<strong>apparaissent plusieurs fois</strong> dans<br/>
l'arbre des appels récursifs, et non qu'ils<br/>
partagent leur solution.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Les sous-problèmes sont triés par taille décroissante</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>L'ordre dans lequel on considère les<br/>
sous-problèmes n'a rien à voir avec leur<br/>
chevauchement. Le mot « chevauchement »<br/>
renvoie à la notion de <strong>doublon</strong>, pas<br/>
d'ordre.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Les sous-problèmes sont indépendants les uns des autres</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Si les sous-problèmes étaient indépendants,<br/>
on appliquerait plutôt le paradigme<br/>
« diviser pour régner ». La programmation<br/>
dynamique exploite précisément le contraire.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>Plusieurs sous-problèmes apparaissent plusieurs fois</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>C'est le cas typique de la suite de<br/>
Fibonacci, où la valeur $\text{fib}(k)$ est<br/>
calculée plusieurs fois dans la version<br/>
naïve. La programmation dynamique évite<br/>
précisément ces recalculs.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Programmation dynamique — Q06 : Sous-structure optimale</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>Que signifie « le problème possède une<br/>
<strong>sous-structure optimale</strong> » ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>La sous-structure optimale et le caractère<br/>
chevauchement des sous-problèmes sont les deux<br/>
ingrédients qui rendent un problème adapté à la<br/>
programmation dynamique. Sans la première, on<br/>
ne peut pas combiner les solutions ; sans la<br/>
seconde, la mémoïsation ne sert à rien.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>La taille des sous-problèmes diminue de manière logarithmique</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Cette description évoque la diminution<br/>
logarithmique du paradigme « diviser pour<br/>
régner » équilibré, mais elle ne correspond<br/>
pas à la définition de la sous-structure<br/>
optimale.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>L'optimum global se compose des optima des sous-problèmes</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Cette propriété permet de construire la<br/>
solution optimale en combinant les solutions<br/>
optimales des sous-problèmes. C'est elle qui<br/>
rend la programmation dynamique applicable.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Le problème peut être résolu en parallèle sur plusieurs processeurs</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>La sous-structure optimale est une propriété<br/>
mathématique du problème, et non une<br/>
caractéristique algorithmique. Le<br/>
parallélisme relève de l'implémentation.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Les sous-problèmes ont tous strictement la même taille</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Cette uniformité de taille n'est pas<br/>
requise. La sous-structure optimale parle de<br/>
la composition des solutions, pas des<br/>
tailles des sous-problèmes.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Programmation dynamique — Q07 : Programmation dynamique et algorithme glouton</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>Quelle est la différence principale entre un<br/>
<strong>algorithme glouton</strong> et un algorithme de<br/>
<strong>programmation dynamique</strong> ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>Quand un problème possède la <strong>propriété de choix glouton</strong> (un choix local mène à<br/>
l'optimum global), un algorithme glouton suffit<br/>
et reste plus efficace. Sinon, la programmation<br/>
dynamique est souvent nécessaire pour<br/>
atteindre l'optimum.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>Le glouton choisit localement, la PD explore les sous-problèmes</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>L'algorithme glouton est plus rapide mais<br/>
peut manquer l'optimum global. La<br/>
programmation dynamique est plus coûteuse,<br/>
mais elle garantit l'optimum global lorsque<br/>
le problème possède la sous-structure<br/>
optimale.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>L'algorithme glouton est récursif, alors que la programmation dynamique est itérative</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Les deux approches peuvent être implémentées<br/>
aussi bien récursivement qu'itérativement.<br/>
C'est le <strong>principe</strong> qui les distingue, et<br/>
non leur forme syntaxique.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>La programmation dynamique est utilisée uniquement pour les jeux vidéo</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>La programmation dynamique a un champ<br/>
d'application très large : alignement de<br/>
séquences, optimisation économique,<br/>
traitement automatique du langage, et bien<br/>
d'autres domaines.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>L'algorithme glouton ne fonctionne que sur les graphes</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Les algorithmes gloutons s'appliquent à de<br/>
très nombreux types de problèmes : rendu de<br/>
monnaie, planification d'activités, sac à<br/>
dos fractionnaire, et bien d'autres.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Programmation dynamique — Q08 : Structure de mémoïsation</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>Quelle structure Python convient le mieux pour<br/>
mémoïser une fonction prenant un seul paramètre<br/>
entier positif ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>Pour des paramètres entiers, la liste est<br/>
idéale. Pour des paramètres plus complexes<br/>
(chaînes, $p$-uplets), le dictionnaire<br/>
devient nécessaire.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>Une liste indexée par la valeur du paramètre</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Pour des paramètres entiers positifs dans<br/>
une plage connue, une liste donne un accès<br/>
en $O(1)$ très efficace. C'est la structure<br/>
la plus rapide.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Un fichier sur le disque</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Le disque est beaucoup trop lent pour de la<br/>
mémoïsation en mémoire. Il s'utiliserait<br/>
plutôt pour persister un cache entre deux<br/>
exécutions du programme.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Un dictionnaire avec les paramètres comme clés</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Un dictionnaire fonctionne, mais avec un<br/>
surcoût lié au calcul des empreintes. Pour<br/>
des indices entiers, la liste reste plus<br/>
rapide.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Une variable globale unique</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Une seule variable ne peut stocker qu'une<br/>
seule valeur, alors que la mémoïsation<br/>
demande d'en mémoriser plusieurs.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Programmation dynamique — Q09 : Origine du terme</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>Le terme « programmation dynamique » a été<br/>
introduit en $1953$ par :</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>Bellman a expliqué qu'il avait choisi le terme<br/>
« dynamique » pour évoquer le caractère<br/>
temporel de la décision, et « programmation »<br/>
au sens de planification. L'équation de Bellman<br/>
reste aujourd'hui un outil central en<br/>
optimisation et en apprentissage par<br/>
renforcement.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Tim Berners-Lee</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Tim Berners-Lee est l'inventeur du World<br/>
Wide Web. Son travail n'a aucun rapport<br/>
avec la programmation dynamique.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Alan Turing</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Alan Turing est principalement connu pour<br/>
la machine de Turing et le décryptage<br/>
d'Enigma, mais pas pour la programmation<br/>
dynamique.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Donald Knuth</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Donald Knuth est connu pour son traité<br/>
monumental « The Art of Computer<br/>
Programming », mais ce n'est pas lui qui<br/>
est à l'origine de la programmation<br/>
dynamique.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>Richard Bellman</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Richard Bellman a inventé la programmation<br/>
dynamique pour traiter des problèmes<br/>
d'optimisation séquentielle. Le mot<br/>
« programmation » vient du jargon militaire<br/>
de l'époque, dans le sens de<br/>
« planification ».</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Programmation dynamique — Q10 : Exemple classique de programmation dynamique</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>Lequel des problèmes suivants se résout<br/>
typiquement par programmation dynamique ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>Autres exemples typiques : sac à dos,<br/>
alignement de séquences (plus longue<br/>
sous-séquence commune, distance<br/>
d'édition). Tous ont en commun la<br/>
sous-structure optimale et les<br/>
sous-problèmes qui se chevauchent.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Tester si un nombre est premier</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Les tests de primalité ne se prêtent pas à<br/>
la programmation dynamique, car ils ne font<br/>
pas apparaître naturellement de<br/>
sous-problèmes qui se chevauchent.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Inverser une chaîne de caractères</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>C'est une opération en $O(n)$ qui ne<br/>
requiert aucune mémoïsation. Aucune<br/>
décomposition en sous-problèmes ne<br/>
s'impose.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Trier un très grand tableau</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Les algorithmes de tri usuels (tri par<br/>
insertion, par sélection, par fusion) ne<br/>
relèvent pas de la programmation<br/>
dynamique.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>Rendre la monnaie avec le moins de pièces</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Le nombre minimal de pièces pour une<br/>
somme $s$ se déduit des sommes plus<br/>
petites $s - p$, pour chaque pièce $p$.<br/>
Ces sous-problèmes se chevauchent : on<br/>
mémorise leurs résultats dans un<br/>
tableau pour ne les calculer qu'une<br/>
fois.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Programmation dynamique — Q11 : Mémoïsation manuelle de Fibonacci</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>On écrit la fonction suivante :</p>
<pre><code>memo = {}
def fib(n):
    if n in memo:
        return memo[n]
    if n &lt;= 1:
        return n
    memo[n] = fib(n - 1) + fib(n - 2)
    return memo[n]</code></pre>
<p>Combien d'appels effectifs (qui calculent<br/>
réellement, sans utiliser le cache) sont<br/>
effectués pour <code>fib(10)</code> ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>La mémoïsation transforme la complexité<br/>
$O(2^n)$ en $O(n)$ : c'est un gain<br/>
algorithmique considérable. Le code reste<br/>
pourtant très proche de la version récursive<br/>
naïve.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Exactement un</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Il faut bien calculer chaque valeur<br/>
intermédiaire au moins une fois. La<br/>
mémoïsation évite les recalculs, pas les<br/>
calculs initiaux.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>Environ onze</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Avec le cache, chaque <code>fib(n)</code> n'est calculée<br/>
qu'une fois pour $n \geq 2$ (les cas de base<br/>
<code>fib(0)</code> et <code>fib(1)</code> ne sont pas mémoïsés),<br/>
soit une douzaine d'appels effectifs. Les<br/>
appels sur une valeur déjà calculée sont<br/>
satisfaits immédiatement par <code>memo</code>, ce qui<br/>
évite l'explosion exponentielle.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Plus de mille</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Cet ordre de grandeur correspond à la<br/>
version sans mémoïsation. Avec un cache,<br/>
le nombre de calculs est bien plus<br/>
modeste.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Environ cent</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>C'est trop. Le nombre de valeurs distinctes<br/>
à calculer est seulement $n + 1$, soit<br/>
$11$, et non $n^2$.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Programmation dynamique — Q12 : Tabulation pour Fibonacci</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>Quelle est la version <strong>itérative ascendante</strong> de<br/>
la suite de Fibonacci ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>L'approche itérative ascendante évite la récursion et le<br/>
cache. Pour Fibonacci, on peut même optimiser<br/>
l'espace en ne gardant que les deux dernières<br/>
valeurs : <code>a, b = b, a + b</code>.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="0" format="html">
    <text><![CDATA[<pre><code>def fib(n):
    return round((1.618 ** n) / 2.236)</code></pre>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Cette formule est celle de Binet, qui<br/>
utilise le nombre d'or. Elle ne relève pas<br/>
de la programmation dynamique et devient<br/>
imprécise pour de grandes valeurs de $n$ à<br/>
cause des arrondis sur les flottants.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="100" format="html">
    <text><![CDATA[<pre><code>def fib(n):
    t = [0, 1]
    for i in range(2, n + 1):
        t.append(t[i-1] + t[i-2])
    return t[n]</code></pre>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>On remplit un tableau <code>t</code> de taille $n + 1$<br/>
en partant des cas de base ($t[0] = 0$ et<br/>
$t[1] = 1$), puis on construit les valeurs<br/>
suivantes par récurrence. La complexité<br/>
est $O(n)$ en temps comme en espace.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<pre><code>def fib(n):
    if n &lt;= 1: return n
    return fib(n-1) + fib(n-2)</code></pre>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Il s'agit de la version récursive <strong>naïve</strong>,<br/>
sans mémoïsation ni tabulation. Sa<br/>
complexité est $O(2^n)$.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<pre><code>def fib(n, memo={}):
    if n in memo: return memo[n]
    if n &lt;= 1: return n
    memo[n] = fib(n-1, memo) + fib(n-2, memo)
    return memo[n]</code></pre>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Il s'agit bien de programmation dynamique,<br/>
mais <strong>descendante</strong> : la fonction reste<br/>
récursive, part de $n$ et mémoïse les<br/>
résultats en redescendant vers les cas de<br/>
base. La version ascendante remplit un<br/>
tableau des cas de base vers $n$, sans<br/>
aucune récursion.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Programmation dynamique — Q13 : Problème du sac à dos</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>Le problème du <strong>sac à dos $0/1$</strong> consiste à<br/>
choisir un sous-ensemble d'objets à mettre dans<br/>
un sac de capacité fixée pour maximiser leur<br/>
valeur totale. Quelle est sa complexité en<br/>
programmation dynamique avec un sac de<br/>
capacité $W$ et $n$ objets ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>Le tableau compte une case par objet et<br/>
par capacité de $0$ à $W$ : son<br/>
remplissage demande donc de l'ordre de<br/>
$n \times W$ opérations, bien moins que<br/>
les $2^n$ sous-ensembles d'une recherche<br/>
exhaustive tant que $W$ reste<br/>
raisonnable.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>$O(n!)$</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Aucune raison particulière n'amène à une<br/>
complexité factorielle pour ce problème.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>$O(n)$</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Il faut considérer chaque objet pour chaque<br/>
capacité possible du sac. La complexité<br/>
dépend nécessairement des deux paramètres.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>$O(2^n)$</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Cette complexité correspond à l'approche<br/>
par recherche exhaustive (essayer tous les<br/>
sous-ensembles d'objets). La programmation<br/>
dynamique est bien plus rapide.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>$O(n \cdot W)$</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>On remplit un tableau à deux dimensions de<br/>
taille $n \times W$. Pour chaque case, le<br/>
calcul est en $O(1)$. La complexité totale<br/>
est donc $O(nW)$.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Programmation dynamique — Q14 : Sac à dos : comparer les deux approches</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>Pour le problème du sac à dos $0/1$,<br/>
l'algorithme glouton qui prend les objets par<br/>
ordre décroissant du ratio « valeur / poids »<br/>
donne-t-il toujours la solution optimale ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>Le sac à dos $0/1$ ne possède <strong>pas</strong> la<br/>
propriété de choix glouton. Il faut donc<br/>
explorer plusieurs possibilités, ce que fait la<br/>
programmation dynamique en $O(nW)$, à comparer<br/>
aux $O(2^n)$ de la recherche exhaustive.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>L'algorithme glouton est plus rapide mais moins précis</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Il s'agit ici d'optimum exact, pas<br/>
d'approximation. Soit l'algorithme garantit<br/>
l'optimum, soit il ne le garantit pas. Il<br/>
n'y a pas de notion de précision<br/>
intermédiaire.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>Non, le glouton peut manquer l'optimum en $0/1$</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Pour le sac à dos $0/1$, l'algorithme<br/>
glouton peut être trompé. Avec un sac de<br/>
capacité $50$ et les objets<br/>
$\{(60, 10), (100, 20), (120, 30)\}$<br/>
(valeur, poids), le glouton prend d'abord<br/>
$(60, 10)$ puis $(100, 20)$ (ratios $6$ puis<br/>
$5$), pour une valeur de $160$. Or l'optimum<br/>
est $(100, 20) + (120, 30)$, de poids $50$ et<br/>
de valeur $220$.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Oui, c'est l'algorithme correct pour ce problème</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Cette approche gloutonne est optimale pour<br/>
le <strong>sac à dos fractionnaire</strong> (où l'on<br/>
peut couper un objet), mais pas pour la<br/>
version $0/1$, où chaque objet est pris<br/>
entièrement ou pas du tout.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Cela dépend du langage de programmation utilisé</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>La propriété est purement mathématique. Elle<br/>
ne dépend pas du langage choisi pour<br/>
l'implémentation.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Programmation dynamique — Q15 : Approche descendante et approche ascendante</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>Quelle est la différence entre l'approche<br/>
<strong>descendante</strong> et l'approche <strong>ascendante</strong> en<br/>
programmation dynamique ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>L'approche descendante (mémoïsation) est plus<br/>
naturelle quand la définition du problème est<br/>
récursive. L'approche ascendante (tabulation)<br/>
est en général plus économe en mémoire et plus<br/>
rapide en pratique. Le choix dépend du<br/>
contexte.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>La descendante est récursive, l'ascendante itérative</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>L'approche descendante part du problème<br/>
global et descend dans les sous-problèmes,<br/>
en utilisant un cache. L'approche<br/>
ascendante part des cas de base et<br/>
construit progressivement la table des<br/>
résultats jusqu'au problème global.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>L'approche descendante concerne les graphes, l'approche ascendante concerne les listes</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Les deux approches s'appliquent à toutes<br/>
les structures de données. La distinction<br/>
n'a rien à voir avec le type des éléments<br/>
manipulés.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>L'approche descendante est toujours plus rapide</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>En pratique, l'approche ascendante est<br/>
souvent <strong>plus rapide</strong>, car elle évite le<br/>
surcoût des appels récursifs. La complexité<br/>
asymptotique est cependant la même dans les<br/>
deux cas.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Aucune, ces termes sont synonymes</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Ce sont deux approches distinctes, même si<br/>
elles permettent toutes deux de résoudre<br/>
les mêmes problèmes.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Programmation dynamique — Q16 : Deux variables au lieu d'un tableau</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>Voici deux calculs ascendants du terme de rang $n$ de la suite de<br/>
Fibonacci :</p>
<pre><code>def fib_tableau(n):
    f = [0 for k in range(n + 1)]
    if n &gt; 0:
        f[1] = 1
    for k in range(2, n + 1):
        f[k] = f[k - 1] + f[k - 2]
    return f[n]

def fib_deux(n):
    a = 0
    b = 1
    for k in range(n):
        c = a + b
        a = b
        b = c
    return a</code></pre>
<p>Pour un grand $n$, que peut-on dire de la mémoire utilisée par ces<br/>
deux fonctions ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>Une programmation dynamique ascendante n'a pas toujours besoin de<br/>
garder tout son tableau : quand chaque valeur ne dépend que d'un<br/>
petit nombre de valeurs calculées juste avant, on peut ne conserver que<br/>
celles-ci. Le temps reste le même, la mémoire devient constante.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Toutes deux utilisent une mémoire proportionnelle à $n$, chacune faisant $n$ tours</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Tu confonds temps et mémoire : les deux fonctions font bien de<br/>
l'ordre de $n$ tours de boucle, mais <code>fib_deux</code> réutilise à chaque<br/>
tour les mêmes trois variables au lieu de remplir un tableau.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Toutes deux utilisent une mémoire constante, aucune n'étant récursive</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Tu ne regardes que la pile d'appels : <code>fib_tableau</code> n'est pas<br/>
récursive, mais elle construit une liste de $n + 1$ cases, ce qui<br/>
occupe une mémoire proportionnelle à $n$.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>Proportionnelle à $n$ pour <code>fib_tableau</code>, constante pour <code>fib_deux</code></p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Exactement : chaque terme ne dépend que des deux termes qui le<br/>
devancent. <code>fib_deux</code> ne garde que ces deux valeurs et un intermédiaire,<br/>
alors que <code>fib_tableau</code> conserve tous les termes calculés.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p><code>fib_deux</code> utilise plus de mémoire, avec trois variables contre une</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Tu comptes les noms au lieu des valeurs stockées : la seule<br/>
variable <code>f</code> de <code>fib_tableau</code> désigne une liste de $n + 1$<br/>
nombres, bien plus que les trois nombres de <code>fib_deux</code>.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Programmation dynamique — Q17 : Rendu de monnaie en programmation dynamique</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>Pour le <strong>rendu de monnaie</strong> avec un système de<br/>
pièces arbitraire, la programmation dynamique<br/>
calcule, en $O(n \cdot s)$ (où $s$ est la somme<br/>
à rendre et $n$ le nombre de pièces) :</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>Le rendu de monnaie est l'exemple emblématique<br/>
d'un problème où l'algorithme glouton <strong>échoue</strong><br/>
sur certains systèmes monétaires (par exemple<br/>
$\{1, 3, 4\}$), alors que la programmation<br/>
dynamique garantit l'optimum.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>La somme totale rendue</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>La somme à rendre est donnée en entrée du<br/>
problème, elle n'est pas calculée par<br/>
l'algorithme.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>Le nombre minimal de pièces pour la somme</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Pour chaque sous-somme $k \in [0, s]$,<br/>
l'algorithme calcule le nombre minimal de<br/>
pièces nécessaires. La récurrence utilisée<br/>
est<br/>
$\text{ND}(k) = 1 + \min_{p}\, \text{ND}(k - p)$,<br/>
où $p$ parcourt les pièces disponibles.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>La pièce la plus grande utilisée</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Cette description correspond plutôt à<br/>
l'approche gloutonne, qui choisit toujours<br/>
la plus grande pièce disponible.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Une séquence ordonnée de pièces</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>La programmation dynamique calcule un<br/>
nombre minimal, et non une séquence<br/>
ordonnée. La séquence des pièces<br/>
effectivement utilisées peut être<br/>
reconstruite ensuite, par un parcours à<br/>
rebours du tableau.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Programmation dynamique — Q18 : Programmation dynamique et diviser pour régner</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>En quoi la programmation dynamique se<br/>
distingue-t-elle du paradigme « diviser pour<br/>
régner » classique ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>Pour identifier si un problème relève de la<br/>
programmation dynamique, on peut tracer<br/>
l'arbre des appels récursifs et chercher les<br/>
nœuds dupliqués. S'il y en a beaucoup, la<br/>
programmation dynamique est probablement le<br/>
bon outil.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Le paradigme « diviser pour régner » utilise toujours la récursivité, contrairement à la programmation dynamique</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Les deux paradigmes peuvent utiliser la<br/>
récursivité. La programmation dynamique<br/>
peut aussi être itérative (par tabulation),<br/>
mais ce n'est pas la différence<br/>
essentielle.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>La programmation dynamique ne s'applique qu'aux problèmes portant sur des entiers</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>La programmation dynamique s'applique à<br/>
tout problème possédant les deux propriétés<br/>
requises (sous-structure optimale et<br/>
sous-problèmes qui se chevauchent), quel que soit<br/>
le type des données manipulées.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>La programmation dynamique est plus simple à coder</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>C'est subjectif et souvent l'inverse. Les<br/>
algorithmes de type « diviser pour régner »<br/>
sont en général plus simples à concevoir.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>En PD les sous-problèmes se recouvrent, pas en diviser-pour-régner</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>C'est la distinction fondamentale. Sans<br/>
recouvrement, la mémoïsation n'apporte<br/>
rien. Avec recouvrement, elle apporte des<br/>
gains majeurs.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Programmation dynamique — Q19 : Compromis mémoire et temps</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>La programmation dynamique illustre un<br/>
compromis classique en algorithmique. Lequel ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>Pour la suite de Fibonacci, la version naïve a<br/>
une complexité en $O(2^n)$ en temps avec<br/>
$O(n)$ d'espace de pile. La version mémoïsée<br/>
passe à $O(n)$ en temps avec $O(n)$ de mémoire :<br/>
on a légèrement augmenté l'espace, mais on a<br/>
divisé le temps par un facteur exponentiel.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Moins de mémoire consommée et moins de temps de calcul</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Il n'y a pas de gain gratuit. Ne stocker<br/>
aucune valeur intermédiaire reviendrait au<br/>
cas naïf récursif, qui est précisément<br/>
beaucoup plus lent.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>Plus de mémoire, beaucoup moins de temps</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>On stocke les solutions intermédiaires<br/>
(mémoire en plus) pour ne plus avoir à les<br/>
recalculer (temps en moins). C'est un<br/>
compromis central en algorithmique.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Aucun changement par rapport à l'algorithme naïf</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>La programmation dynamique apporte un gain<br/>
majeur sur de nombreux problèmes (suite de<br/>
Fibonacci, sac à dos, plus longue<br/>
sous-séquence commune, etc.).</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Plus de temps de calcul mais moins de mémoire consommée</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>C'est exactement l'inverse. La<br/>
programmation dynamique consomme plus de<br/>
mémoire, mais elle accélère<br/>
considérablement le calcul.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Programmation dynamique — Q20 : Lecture d'une programmation dynamique ascendante</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>Que calcule la fonction Python suivante ?</p>
<pre><code>def f(n):
    T = [0] * (n + 1)
    T[0] = 1
    for i in range(1, n + 1):
        T[i] = T[i-1] * i
    return T[n]</code></pre>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>Cet exemple illustre une <strong>programmation dynamique ascendante</strong> très simple : on<br/>
remplit un tableau en partant du cas de base.<br/>
C'est l'archétype de la tabulation pour une<br/>
récurrence simple.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>La factorielle $n!$</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>On a $T[i] = T[i-1] \cdot i = i!$, en<br/>
partant de $T[0] = 1 = 0!$. La fonction<br/>
renvoie donc bien $n!$, par construction<br/>
ascendante.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>La $n$-ième valeur de la suite de Fibonacci</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>La suite de Fibonacci utiliserait<br/>
l'instruction <code>T[i] = T[i-1] + T[i-2]</code>,<br/>
fondée sur une addition de deux termes<br/>
précédents, et non sur la multiplication<br/>
par $i$.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>$2^n$</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Pour obtenir $2^n$, il faudrait écrire<br/>
<code>T[i] = T[i-1] <em> 2</code> plutôt que<br/>
<code>T[i-1] </em> i</code>.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>La somme $1 + 2 + \cdots + n$</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Une somme s'obtiendrait par addition. Or<br/>
la fonction effectue une multiplication<br/>
(<code>T[i-1] * i</code>), qui correspond à un<br/>
produit cumulé.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Programmation dynamique — Q22 : Découper une barre au meilleur prix</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>Un menuisier vend des morceaux de barre : <code>prix[m]</code> est le prix,<br/>
en euros, d'un morceau de longueur <code>m</code>. La fonction suivante<br/>
calcule le meilleur prix d'une barre de longueur <code>n</code> que l'on peut<br/>
couper en morceaux :</p>
<pre><code>def meilleur_prix(n, prix):
    t = [0 for k in range(n + 1)]
    for k in range(1, n + 1):
        meilleur = 0
        for m in range(1, k + 1):
            if prix[m] + t[k - m] &gt; meilleur:
                meilleur = prix[m] + t[k - m]
        t[k] = meilleur
    return t[n]</code></pre>
<p>Que renvoie <code>meilleur_prix(4, [0, 1, 5, 8, 9])</code> ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>La case <code>t[k]</code> contient le meilleur prix d'une barre de longueur<br/>
<code>k</code>. On la calcule en essayant chaque longueur <code>m</code> du premier<br/>
morceau, le reste valant <code>t[k - m]</code>, déjà calculé. Chaque<br/>
sous-problème n'est résolu qu'une fois.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>$4$ euros</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : $4$ euros correspond à quatre morceaux de longueur $1$.<br/>
La boucle interne essaie aussi des premiers morceaux de longueur<br/>
$2$, $3$ ou $4$, bien plus rentables.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>$8$ euros</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : $8$ est la valeur de <code>t[3]</code>, le meilleur prix d'une<br/>
barre de longueur $3$. La fonction renvoie <code>t[4]</code>, calculé au<br/>
dernier tour de la boucle externe.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>$9$ euros</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : $9$ euros, c'est la barre vendue d'un seul tenant, ou<br/>
coupée en $3 + 1$. Pour <code>k = 4</code>, la fonction essaie aussi<br/>
<code>prix[2] + t[2]</code>, qui vaut $5 + 5 = 10$.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>$10$ euros</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Bonne réponse : le tableau se remplit en <code>[0, 1, 5, 8, 10]</code>.<br/>
Pour <code>k = 4</code>, les candidats valent $1 + 8$, $5 + 5$, $8 + 1$ et<br/>
$9 + 0$ : le meilleur est $10$, avec deux morceaux de longueur<br/>
$2$.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Programmation dynamique — Q23 : Reconstruire la solution</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>En programmation dynamique, on calcule souvent<br/>
une <strong>valeur optimale</strong> (par exemple le coût<br/>
minimal). Comment retrouve-t-on la <strong>solution effective</strong>, c'est-à-dire la séquence de<br/>
décisions qui mène à cet optimum ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>Cette technique de reconstruction à rebours<br/>
est universelle en programmation dynamique :<br/>
après avoir rempli la table, on retrace les<br/>
choix faits à chaque étape pour reconstruire<br/>
la solution complète.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>On ne peut pas, la programmation dynamique ne donne que la valeur optimale</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>La solution effective peut tout à fait<br/>
être reconstruite : c'est ce qui rend la<br/>
programmation dynamique utile dans les<br/>
applications réelles.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>On lance plusieurs programmations dynamiques avec différents paramètres pour identifier les choix</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Cette approche serait inefficace. Une seule<br/>
exécution suffit, à condition de bien lire<br/>
la table à rebours.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>On la reconstruit en parcourant la table à rebours, à partir de la valeur finale</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>On remonte dans la table en suivant les<br/>
choix qui ont mené à chaque valeur<br/>
optimale. Cela permet de retrouver la<br/>
séquence de décisions sans alourdir le<br/>
calcul lui-même.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>On stocke aussi la solution dans la table</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>C'est possible mais coûteux en mémoire, et<br/>
souvent inutile : la solution se<br/>
reconstruit toujours en $O(n)$ après le<br/>
calcul, par un parcours à rebours.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Programmation dynamique — Q25 : Reconnaître un problème de programmation dynamique</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>Pour qu'un problème se résolve efficacement par<br/>
programmation dynamique, il doit posséder :</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>Ces deux propriétés sont l'identité de la<br/>
programmation dynamique. Sans la sous-structure<br/>
optimale, il est impossible de combiner les<br/>
solutions. Sans le recouvrement des<br/>
sous-problèmes, la mémoïsation est inutile, et<br/>
l'on retombe sur le paradigme « diviser pour<br/>
régner » classique.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Une décomposition logarithmique du problème</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Cette caractéristique correspond plutôt au<br/>
paradigme « diviser pour régner »<br/>
classique. Ce n'est pas un prérequis de la<br/>
programmation dynamique.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Un seul cas de base et une seule récurrence</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Le nombre de cas de base ou de récurrences<br/>
n'est pas un critère. Ce sont les<br/>
propriétés structurelles du problème qui<br/>
comptent.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Une fonction objectif uniquement de minimisation</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>La programmation dynamique s'applique aussi<br/>
à des problèmes de maximisation (sac à dos,<br/>
plus longue sous-séquence) ou de comptage<br/>
(nombre de chemins).</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>Sous-structure optimale et sous-problèmes répétés</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Ces deux propriétés sont nécessaires. La<br/>
sous-structure optimale permet de combiner<br/>
les solutions, et les sous-problèmes qui<br/>
se chevauchent rendent la mémoïsation<br/>
utile.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Programmation dynamique — Q26 : Chemins dans une grille</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>On veut compter le nombre de chemins<br/>
monotones (n'allant qu'à droite ou en bas)<br/>
d'un coin d'une grille rectangulaire de $m$<br/>
lignes et $n$ colonnes au coin opposé. Quelle<br/>
est la récurrence appropriée et la complexité<br/>
par programmation dynamique ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>Ce problème est l'un des plus simples<br/>
illustrant la programmation dynamique sur une<br/>
table à deux dimensions. La même technique<br/>
s'étend aux variantes (cases bloquées,<br/>
sommes minimales, etc.).</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>$C(i, j) = C(i-1, j) + C(i, j-1)$,<br/>
complexité $O(m \cdot n)$</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Bonne réponse : pour atteindre la case<br/>
$(i, j)$, on vient soit du dessus<br/>
$(i-1, j)$ soit de la gauche $(i, j-1)$.<br/>
Les cas de base sont les bords, où il<br/>
n'existe qu'un seul chemin (descente ou<br/>
déplacement à droite uniquement), d'où<br/>
$C(0, j) = C(i, 0) = 1$. On<br/>
remplit un tableau à deux dimensions de<br/>
taille $m \times n$, chaque case en<br/>
$O(1)$, donc complexité totale<br/>
$O(m \cdot n)$.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>$C(i, j) = C(i-1, j-1) + 1$, complexité<br/>
$O(m \cdot n)$</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : la récurrence proposée ne<br/>
correspond pas au problème. On ne peut se<br/>
déplacer qu'à droite <strong>ou</strong> vers le bas,<br/>
pas en diagonale. La récurrence correcte<br/>
additionne les chemins venant de la case<br/>
du dessus et de la case de gauche.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>$C(i, j) = C(i-1, j) \cdot C(i, j-1)$,<br/>
complexité $O(m + n)$</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : pour compter le nombre de<br/>
chemins, on <strong>additionne</strong> les<br/>
possibilités, on ne les multiplie pas. La<br/>
complexité est aussi sous-estimée :<br/>
remplir un tableau à deux dimensions<br/>
coûte $O(m \cdot n)$, pas $O(m + n)$.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>$C(i, j) = i + j$, calcul direct en<br/>
$O(1)$ sans tableau</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : le nombre de chemins n'est pas la<br/>
somme des coordonnées mais bien le<br/>
coefficient binomial $\binom{i+j}{i}$. Une<br/>
formule fermée existe, mais elle se<br/>
calcule en $O(\min(i, j))$ multiplications,<br/>
pas en temps constant.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Programmation dynamique — Q28 : Trace sur le rendu de monnaie</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>Avec les pièces $\{1, 3, 4\}$ et la somme à<br/>
rendre $s = 6$, quel est le <strong>nombre minimal</strong><br/>
de pièces calculé par programmation dynamique<br/>
(et l'écart avec la méthode gloutonne qui<br/>
prend toujours la plus grosse pièce<br/>
possible) ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>Cet exemple est emblématique : il prouve<br/>
qu'avec un système monétaire arbitraire,<br/>
l'algorithme glouton n'est plus optimal. Avec<br/>
les systèmes monétaires usuels (euros,<br/>
dollars), l'algorithme glouton fonctionne<br/>
grâce à des propriétés structurelles<br/>
particulières des dénominations choisies.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>La programmation dynamique trouve $1$<br/>
pièce(s), l'algorithme glouton en utilise $2$</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : aucune pièce de valeur $6$ n'existe dans le système monétaire $\{1, 3, 4\}$, donc une pièce ne peut pas suffire. Le minimum atteignable est de<br/>
$2$ pièces (combinaison $3 + 3$).</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>La programmation dynamique et l'algorithme<br/>
glouton trouvent tous deux $3$ pièces</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : si c'était le cas, la<br/>
programmation dynamique n'aurait aucun<br/>
intérêt sur ce problème. Refaire le<br/>
calcul : avec $3 + 3$, $2$ pièces<br/>
suffisent, ce que l'algorithme glouton<br/>
rate.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Aucune des deux méthodes n'aboutit, car<br/>
aucune combinaison de ces pièces ne donne $6$</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : la combinaison $3 + 3 = 6$ est<br/>
parfaitement valide. De même<br/>
$1 + 1 + 4 = 6$ ou $1 + 1 + 1 + 3 = 6$<br/>
fonctionnent. Le problème est résoluble.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>La programmation dynamique trouve $2$<br/>
pièces, l'algorithme glouton en utilise $3$</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Bonne réponse : c'est l'exemple<br/>
pédagogique classique où la stratégie<br/>
gloutonne échoue à atteindre l'optimum.<br/>
Glouton : prendre $4$, reste $2$, prendre $1$, prendre $1$, total $3$ pièces<br/>
(combinaison $4 + 1 + 1$).<br/>
Programmation dynamique : explore toutes<br/>
les combinaisons et trouve $3 + 3 = 6$,<br/>
soit seulement $2$ pièces. Cas<br/>
paradigmatique de l'intérêt de la<br/>
programmation dynamique.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Programmation dynamique — Q29 : Trace du sac à dos</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>On dispose d'un sac de capacité $W = 5$ et de<br/>
trois objets : $O_1$ (poids $2$, valeur $3$),<br/>
$O_2$ (poids $3$, valeur $4$), $O_3$ (poids<br/>
$4$, valeur $5$). Quelle est la valeur<br/>
optimale obtenue par programmation<br/>
dynamique ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>Construire le tableau $V$ à la main est un<br/>
excellent exercice. Il révèle que la<br/>
programmation dynamique « teste » toutes les<br/>
combinaisons utiles sans en énumérer<br/>
explicitement aucune. Pour reconstruire la<br/>
solution (les objets choisis), on remonte<br/>
dans le tableau à rebours en regardant<br/>
pour chaque case quelle décision a mené à<br/>
l'optimum.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>$12$ (en prenant les trois objets)</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : prendre les trois objets pèse<br/>
$2 + 3 + 4 = 9 \mathrm{kg}$, bien<br/>
au-delà de la capacité du sac. C'est la<br/>
difficulté du sac à dos : on doit<br/>
renoncer à des objets précieux à cause<br/>
de la contrainte de poids.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>$7$ (en prenant $O_1$ et $O_2$)</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Bonne réponse : on construit la table<br/>
$V[i][w]$ donnant la valeur optimale en<br/>
considérant les $i$ premiers objets pour<br/>
une capacité $w$. Quelques valeurs clés :<br/>
$V[2][5] = \max(V[1][5], V[1][2] + 4) = \max(3, 3 + 4) = 7$. À l'arrivée $V[3][5] = \max(V[2][5], V[2][1] + 5) = \max(7, 0 + 5) = 7$. La solution optimale prend $O_1$ ($2 \mathrm{kg}$, $3$) et $O_2$ ($3 \mathrm{kg}$, $4$), poids total $5$, valeur totale $7$.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>$5$ (en prenant uniquement $O_3$)</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : prendre seulement $O_3$ ne donne<br/>
que $5$ de valeur, alors que la combinaison $O_1 + O_2$ donne $7$ pour un poids identique au sac (capacité atteinte exactement). Il vaut donc mieux<br/>
combiner plusieurs objets.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>$9$ (en prenant $O_2$ et $O_3$)</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : la combinaison $O_2$ + $O_3$ pèse $3 + 4 = 7 \mathrm{kg}$, ce qui dépasse la capacité $W = 5$ du sac. Cette solution n'est donc pas réalisable.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Programmation dynamique — Q30 : Code du sac à dos en programmation dynamique</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>Quel code Python implémente correctement la<br/>
programmation dynamique ascendante (par<br/>
tabulation) pour le sac à dos $0/1$ avec $n$<br/>
objets de poids <code>poids[i]</code> et valeurs<br/>
<code>valeurs[i]</code> et un sac de capacité <code>W</code> ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>Complexité : $O(n \cdot W)$ en temps et en<br/>
espace. Optimisation possible : ne conserver<br/>
que deux lignes du tableau, celle que l'on<br/>
remplit et celle du dessus, ce qui ramène<br/>
l'espace à $O(W)$. Pour<br/>
reconstruire la solution effective, on<br/>
remonte le tableau de la case $V[n][W]$ vers<br/>
la case $V[0][0]$, en notant à chaque<br/>
étape si l'objet $i$ a été pris.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="0" format="html">
    <text><![CDATA[<pre><code>def sac_a_dos(poids, valeurs, W):
    total = 0
    for i in range(len(poids)):
        total += valeurs[i]
    return total</code></pre>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : ce code retourne la somme de<br/>
<strong>toutes</strong> les valeurs sans tenir compte<br/>
de la contrainte de poids. Pour trois<br/>
objets de poids $2$, $3$, $4$ et de<br/>
valeurs $3$, $4$, $5$ avec $W = 5$, il<br/>
renvoie $12$ alors que la valeur optimale<br/>
vaut $7$.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="100" format="html">
    <text><![CDATA[<pre><code>def sac_a_dos(poids, valeurs, W):
    n = len(poids)
    V = [[0] * (W + 1) for _ in range(n + 1)]
    for i in range(1, n + 1):
        for w in range(W + 1):
            if poids[i-1] &lt;= w:
                V[i][w] = max(V[i-1][w],
                              V[i-1][w-poids[i-1]] + valeurs[i-1])
            else:
                V[i][w] = V[i-1][w]
    return V[n][W]</code></pre>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Bonne réponse : on construit un tableau à<br/>
deux dimensions de taille<br/>
$(n+1) \times (W+1)$. Pour chaque case<br/>
$(i, w)$ : si l'objet $i$ rentre dans le<br/>
sac (<code>poids[i-1] &lt;= w</code>), on prend le<br/>
maximum entre « ne pas le prendre »<br/>
(<code>V[i-1][w]</code>) et « le prendre »<br/>
(<code>V[i-1][w-poids[i-1]] + valeurs[i-1]</code>) ;<br/>
sinon, on ne peut pas le prendre. Indices<br/>
$i-1$ car les listes Python commencent<br/>
à $0$.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<pre><code>def sac_a_dos(poids, valeurs, W):
    # Glouton : meilleur ratio valeur/poids d'abord
    couples = []
    for i in range(len(poids)):
        couples.append((valeurs[i] / poids[i], i))
    total = 0
    capa = W
    for ratio, i in sorted(couples, reverse=True):
        if poids[i] &lt;= capa:
            total += valeurs[i]
            capa -= poids[i]
    return total</code></pre>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : c'est l'algorithme <strong>glouton</strong> par<br/>
ratio valeur/poids. Il est optimal pour<br/>
le sac à dos <strong>fractionnaire</strong> (où l'on<br/>
peut couper des objets), mais pas pour<br/>
la version $0/1$. Pour des poids $1$,<br/>
$2$, $3$, des valeurs $6$, $10$, $12$ et<br/>
$W = 5$, il prend l'objet de ratio $6$<br/>
puis celui de ratio $5$ et renvoie $16$,<br/>
alors que les objets de poids $2$ et $3$<br/>
donnent $22$.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<pre><code>def sac_a_dos(poids, valeurs, W):
    n = len(poids)
    V = [[0] * (W + 1) for _ in range(n + 1)]
    for i in range(1, n + 1):
        for w in range(W + 1):
            if poids[i-1] &lt;= w:
                V[i][w] = max(V[i-1][w],
                              V[i][w-poids[i-1]] + valeurs[i-1])
            else:
                V[i][w] = V[i-1][w]
    return V[n][W]</code></pre>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : dans le second terme du <code>max</code>,<br/>
la case consultée est <code>V[i][w-poids[i-1]]</code><br/>
(ligne $i$ au lieu de $i-1$), ce qui<br/>
autorise à reprendre l'objet $i$<br/>
plusieurs fois. C'est le sac à dos<br/>
<strong>non borné</strong>, pas la version $0/1$.<br/>
Avec un seul objet de poids $2$ et de<br/>
valeur $3$ pour $W = 4$, ce code renvoie<br/>
$6$ (l'objet pris deux fois) au lieu de<br/>
$3$.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Programmation dynamique — Q31 : Mesurer le gain de la mémoïsation</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>On compare le calcul de <code>fibonacci(40)</code> avec<br/>
la version récursive naïve et avec la<br/>
version mémoïsée. Quel est l'ordre de<br/>
grandeur du rapport entre les deux<br/>
complexités ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>Cet exemple illustre la puissance de la<br/>
programmation dynamique. Bien sûr, pour<br/>
Fibonacci en pratique, on utilise plutôt la<br/>
version itérative à deux variables, qui a<br/>
la même complexité $O(n)$ mais avec une<br/>
consommation mémoire $O(1)$ au lieu de<br/>
$O(n)$.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Les deux versions ont la même complexité<br/>
asymptotique</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : la mémoïsation change<br/>
fondamentalement la complexité. La<br/>
version naïve calcule plusieurs fois<br/>
les mêmes valeurs (par exemple,<br/>
$\mathrm{fib}(35)$ est calculée<br/>
plusieurs millions de fois) ; la<br/>
mémoïsée ne calcule chaque valeur qu'une<br/>
seule fois.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>La version mémoïsée est environ deux fois<br/>
plus rapide</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : un facteur deux serait une<br/>
amélioration mineure. Or le passage de<br/>
$O(2^n)$ à $O(n)$ représente un gain<br/>
<strong>exponentiel</strong>, pas multiplicatif. Pour<br/>
$n = 40$, c'est un facteur de plus de<br/>
dix milliards.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>La version mémoïsée est plus lente à<br/>
cause du surcoût du cache</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : pour $n$ aussi petit que $20$,<br/>
le surcoût du cache est négligeable<br/>
devant le gain en évitant les calculs<br/>
redondants. Pour $n = 40$, le gain est<br/>
colossal.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>La version mémoïsée est environ<br/>
$10^{10}$ fois plus rapide</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Bonne réponse : version naïve en<br/>
$O(2^n)$, soit environ<br/>
$2^{40} \approx 10^{12}$ appels<br/>
récursifs. Version mémoïsée en $O(n)$,<br/>
soit environ $40$ appels effectifs. Le<br/>
rapport est donc d'environ<br/>
$10^{12} / 40 \approx 2{,}5 \cdot 10^{10}$.<br/>
En pratique, on passe de plusieurs<br/>
minutes à une fraction de milliseconde.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Programmation dynamique — Q32 : Monter un escalier une ou deux marches à la fois</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>Pour monter un escalier de $4$ marches, on avance d'une ou de<br/>
deux marches à chaque pas. Le nombre de façons $E(n)$ vérifie<br/>
$E(n) = E(n-1) + E(n-2)$ avec $E(1) = 1$ et $E(2) = 2$. Combien de<br/>
façons de monter $4$ marches ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>La dernière marche est atteinte soit depuis la précédente, soit<br/>
depuis l'avant-dernière : la récurrence est celle de Fibonacci, et<br/>
la tabulation évite tout recalcul.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>$5$</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Bonne réponse : en remplissant la table de proche en proche,<br/>
$E(3) = 3$, puis $E(4) = 5$, et ainsi de suite jusqu'à<br/>
$E(4) = 5$.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>$3$</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : $3$ est $E(3)$ ; il reste une étape de la<br/>
récurrence.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>$16$</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : $2^{4}$ compterait des suites de pas sans tenir<br/>
compte de la longueur totale.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>$8$</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : $8$ est $E(5)$, une marche de trop.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Programmation dynamique — Q33 : La somme maximale d'une tranche</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>Pour trouver la tranche contiguë de somme maximale d'une liste, on<br/>
calcule pour chaque position $i$ la meilleure somme d'une tranche<br/>
qui se termine en $i$ : $M(i) = t[i] + \max(0, M(i-1))$. Quelle est<br/>
la somme maximale pour <code>[-2, 3, -1, 4, -3]</code> ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>Chaque $M(i)$ se déduit du précédent en temps constant : une seule<br/>
passe suffit, là où tester toutes les tranches coûterait $n^2$.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>$6$, obtenue par la tranche <code>[3, -1, 4]</code></p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Bonne réponse : on prolonge la tranche précédente si sa somme est<br/>
positive, sinon on repart de $t[i]$. Le maximum des $M(i)$ vaut<br/>
$6$.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>$1$, la somme de toute la liste</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : la liste entière contient des valeurs négatives qui<br/>
font baisser la somme ; une tranche plus courte fait mieux.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>$4$, le plus grand élément isolé</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : une tranche peut regrouper plusieurs éléments et<br/>
dépasser le maximum isolé, ou, si tout est négatif, s'y<br/>
réduire.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>$0$, en choisissant la tranche vide</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : la tranche doit contenir au moins un élément.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Programmation dynamique — Q34 : Compter les façons de rendre une somme</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>Avec des pièces de $1$ et de $2$, de combien de façons peut-on<br/>
rendre la somme $4$, l'ordre des pièces ne comptant pas ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>Avec plus de valeurs de pièces, on remplit une table<br/>
$N(s, k)$, nombre de façons de rendre $s$ avec les $k$ premières<br/>
pièces : chaque case se déduit des précédentes.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>$3$</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Bonne réponse : le nombre de pièces de $2$ va de $0$ à<br/>
$2$, le reste étant complété par des $1$ : $1+1+1+1$, $1+1+2$ et $2+2$.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>$4$</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : le nombre de façons n'est pas le montant.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>$8$</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : ce compte supposerait des choix indépendants, alors<br/>
que le total est contraint.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>$1$</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : plusieurs répartitions entre pièces de $1$ et de $2$<br/>
conviennent.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Programmation dynamique — Q35 : Le triangle de Pascal par tabulation</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>Les coefficients binomiaux vérifient<br/>
$C(n, k) = C(n-1, k-1) + C(n-1, k)$ avec $C(n, 0) = C(n, n) = 1$.<br/>
En remplissant la table ligne par ligne, que vaut $C(6, 2)$ ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>Calculer $C(n, k)$ par la récurrence sans table recalcule les mêmes<br/>
valeurs ; la tabulation construit chaque ligne à partir de la<br/>
précédente.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>$15$</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Bonne réponse : $C(6, 2) = C(5, 1) + C(5, 2) =<br/>
5 + 10 = 15$, chaque terme venant de la ligne précédente.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>$8$</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : la récurrence additionne deux coefficients, pas $n$ et<br/>
$k$.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>$12$</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : $6 \times 2$ n'a rien à voir avec la récurrence.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>$10$</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : $10$ est $C(5, 2)$, il manque l'autre terme.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Programmation dynamique — Q36 : Quand la programmation dynamique n'apporte rien</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>Le tri fusion découpe la liste en deux moitiés et les trie<br/>
récursivement. La programmation dynamique peut-elle l'accélérer ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>Diviser pour régner et programmation dynamique se distinguent par<br/>
le chevauchement des sous-problèmes : absent dans le premier,<br/>
central dans la seconde.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>Non, aucun sous-problème n'y est résolu deux fois</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Bonne réponse : chaque moitié n'est triée qu'une fois. Sans<br/>
chevauchement de sous-problèmes, mémoriser les résultats ne sert<br/>
à rien.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Oui, en mémorisant chaque fusion dans un dictionnaire</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : chaque fusion n'est faite qu'une fois ; la mémoriser<br/>
coûterait sans rien économiser.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Oui, tout algorithme récursif en profite de la même façon</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : seuls ceux dont les sous-problèmes se répètent en<br/>
profitent.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Non, car le tri fusion n'est pas un algorithme récursif</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : il l'est ; la raison est l'absence de chevauchement.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Programmation dynamique — Q37 : Combien de fois le même sous-problème</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>Dans le calcul récursif naïf de <code>fib(6)</code>, combien de fois<br/>
<code>fib(2)</code> est-il calculé ?<br/>
<pre><code>def fib(n):
    if n &lt;= 1:
        return n
    return fib(n - 1) + fib(n - 2)</code></pre></p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>Le chevauchement des sous-problèmes se mesure : ici les petits<br/>
termes sont recalculés un nombre exponentiel de fois.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>$5$ fois</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Bonne réponse : le nombre d'appels à <code>fib(2)</code> dans <code>fib(n)</code> est<br/>
$F(n-1)$, le terme de Fibonacci lui-même : $5$ pour<br/>
$n = 6$. Un seul calcul suffirait.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>$1$ fois</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : c'est le cas avec mémoïsation ; sans elle, l'arbre<br/>
d'appels répète ce sous-problème.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>$12$ fois</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : le compte ne suit pas une loi linéaire en $n$.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>$6$ fois</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : le compte suit la suite de Fibonacci, pas $n$.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Programmation dynamique — Q38 : Le point de départ d'une solution dynamique</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>Par quoi commence la conception d'une solution par programmation<br/>
dynamique ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>Récurrence, table, ordre de remplissage, lecture du résultat : les<br/>
quatre temps d'une programmation dynamique.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>Par une relation de récurrence entre sous-problèmes</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Bonne réponse : sans récurrence, rien à mémoriser ni à tabuler.<br/>
Le choix de la table et l'ordre de remplissage en découlent.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Par le choix du langage et des structures de données</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : la méthode est indépendante du langage.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Par la mesure du temps d'exécution de la version naïve</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : mesurer peut motiver, mais ne construit pas la<br/>
solution.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Par le tri des données d'entrée par ordre croissant</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : le tri n'est pas une étape générale de la méthode.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Programmation dynamique — Q39 : Mémoïser une fonction à deux arguments</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>La fonction <code>valeur(i, w)</code> du sac à dos dépend de deux entiers.<br/>
Comment organiser sa mémoïsation avec un dictionnaire <code>memo</code> ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>La clé de mémoïsation doit déterminer entièrement l'appel : un<br/>
tuple de tous les arguments, ou une table à deux dimensions.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>Avec la clé <code>(i, w)</code>, un tuple</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Bonne réponse : un tuple est immuable et identifie le couple<br/>
d'arguments ; <code>memo[(i, w)]</code> retrouve le résultat déjà calculé.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Avec la clé <code>i</code> seulement, <code>w</code> variant peu</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : deux appels de même <code>i</code> et de <code>w</code> différents auraient<br/>
des résultats différents mais la même clé.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Avec la clé <code>i + w</code>, la somme des deux</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : <code>(2, 3)</code> et <code>(3, 2)</code> auraient la même clé pour des<br/>
résultats différents.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Avec une liste <code>[i, w]</code> comme clé</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : une liste n'est pas utilisable comme clé de<br/>
dictionnaire.</p>]]></text>
    </feedback>
  </answer>
</question>

</quiz>
