{
  "chapter": {
    "id": "graphes",
    "level": "terminale",
    "theme": "Structures de données",
    "title": "Graphes",
    "description": "Définition d'un graphe (sommets, arêtes), graphes orientés\net non orientés, pondérés, représentations (matrice\nd'adjacence, liste d'adjacence), parcours en largeur\net en profondeur, plus court chemin, applications.",
    "prerequisites": [],
    "references": []
  },
  "questions": [
    {
      "id": "q01",
      "difficulty": 1,
      "skills": [
        "definition"
      ],
      "title": "Définition d'un graphe",
      "statement": "Qu'est-ce qu'un **graphe** en informatique ?",
      "options": [
        {
          "text": "Un type d'arbre",
          "correct": false,
          "feedback": "Erreur : c'est plutôt l'inverse. Un arbre est un\ncas particulier de graphe (acyclique connexe).\n"
        },
        {
          "text": "Une représentation visuelle de données numériques (camembert, histogramme)",
          "correct": false,
          "feedback": "Erreur : ce serait un **graphique**. Un graphe en\ninformatique est une structure mathématique\ndifférente.\n"
        },
        {
          "text": "Des sommets reliés par des arêtes",
          "correct": true,
          "feedback": "Bonne réponse : c'est la définition mathématique\nstandard. Un graphe modélise n'importe quelle\nrelation entre objets : réseau routier, réseau\nsocial, dépendances de tâches, etc.\n"
        },
        {
          "text": "Un dictionnaire trié",
          "correct": false,
          "feedback": "Un dictionnaire est une\nstructure associant des\nclés à des valeurs, sans\nrapport avec un ensemble\nde sommets reliés par des\narêtes.\n"
        }
      ],
      "explanation": "Les graphes sont omniprésents : transport, réseaux\nsociaux, web (pages reliées par hyperliens), réseaux\nélectriques, planification de tâches, recommandations.\nConnaître les algorithmes de graphes est essentiel."
    },
    {
      "id": "q02",
      "difficulty": 1,
      "skills": [
        "oriente"
      ],
      "title": "Graphe orienté",
      "statement": "Quelle est la différence entre un graphe **orienté** et\nun graphe **non orienté** ?",
      "options": [
        {
          "text": "L'orienté a des arêtes à sens, pas le non orienté",
          "correct": true,
          "feedback": "Bonne réponse : on parle d'**arcs** pour les\narêtes orientées. Exemple orienté : suivre\nquelqu'un sur Twitter (asymétrique). Exemple non\norienté : amitié sur Facebook (symétrique).\n"
        },
        {
          "text": "Un graphe orienté n'a pas de cycle",
          "correct": false,
          "feedback": "Erreur : un graphe orienté **peut** avoir des\ncycles. Sans cycle, on parle de DAG (graphe\norienté acyclique).\n"
        },
        {
          "text": "Le graphe orienté contient plus de sommets",
          "correct": false,
          "feedback": "Erreur : aucun rapport avec le nombre de sommets.\n"
        },
        {
          "text": "Un graphe non orienté est toujours plus rapide à parcourir",
          "correct": false,
          "feedback": "Erreur : la vitesse de parcours dépend du nombre\nde sommets et arêtes, pas de l'orientation.\n"
        }
      ],
      "explanation": "Cas typique : réseau routier (orienté si rues à sens\nunique, non orienté si toutes en double sens). Le\nweb est orienté (un lien va d'une page à l'autre\nsans symétrie)."
    },
    {
      "id": "q03",
      "difficulty": 1,
      "skills": [
        "pondere"
      ],
      "title": "Graphe pondéré",
      "statement": "Qu'est-ce qu'un graphe **pondéré** ?",
      "options": [
        {
          "text": "Un graphe avec beaucoup d'arêtes",
          "correct": false,
          "feedback": "Erreur : c'est plutôt un graphe **dense**. Aucun\nrapport avec « pondéré ».\n"
        },
        {
          "text": "Un graphe partagé entre plusieurs utilisateurs",
          "correct": false,
          "feedback": "Le partage entre\nutilisateurs relève d'un\naspect logiciel sans\nrapport avec la\npondération des arêtes.\n"
        },
        {
          "text": "Un graphe trop lourd à stocker en mémoire",
          "correct": false,
          "feedback": "Erreur : « pondéré » est un terme technique précis,\npas une métaphore.\n"
        },
        {
          "text": "Un graphe où chaque arête porte un poids",
          "correct": true,
          "feedback": "Bonne réponse : par exemple, un GPS représente\nles routes par un graphe pondéré (poids = temps\nde trajet). Les algorithmes de plus court chemin\n(Dijkstra) exploitent ces poids.\n"
        }
      ],
      "explanation": "Un graphe peut être à la fois orienté ET pondéré\n(cas d'un GPS, d'un réseau de transport, etc.)."
    },
    {
      "id": "q04",
      "difficulty": 1,
      "skills": [
        "matrice-adjacence"
      ],
      "title": "Matrice d'adjacence",
      "statement": "Une **matrice d'adjacence** d'un graphe à $n$ sommets est :",
      "options": [
        {
          "text": "Une matrice $n \\times n$ avec $M[i][j]=1$ si arête $i$-$j$",
          "correct": true,
          "feedback": "Bonne réponse : c'est l'une des deux représentations\nprincipales des graphes. Avantage : test\nd'arête en $O(1)$. Inconvénient : consomme\n$O(n^2)$ d'espace, même pour des graphes creux.\n"
        },
        {
          "text": "Un tableau $1 \\times n$ contenant tous les degrés des sommets",
          "correct": false,
          "feedback": "Erreur : ce serait un tableau de degrés, pas une\nmatrice d'adjacence.\n"
        },
        {
          "text": "Un arbre",
          "correct": false,
          "feedback": "Un arbre est une\nstructure de données\nspécifique, sans rapport\navec la représentation\nd'un graphe par une\nmatrice.\n"
        },
        {
          "text": "Un dictionnaire de listes",
          "correct": false,
          "feedback": "Erreur : c'est plutôt une **liste d'adjacence**.\n"
        }
      ],
      "explanation": "Pour un graphe non orienté, la matrice est\n**symétrique** ($M[i][j] = M[j][i]$). Pour un\ngraphe orienté, elle peut ne pas l'être."
    },
    {
      "id": "q05",
      "difficulty": 1,
      "skills": [
        "liste-adjacence"
      ],
      "title": "Liste d'adjacence",
      "statement": "Une **liste d'adjacence** d'un graphe consiste à :",
      "options": [
        {
          "text": "Stocker uniquement les sommets isolés du graphe entier",
          "correct": false,
          "feedback": "Erreur : on stocke tous les sommets.\n"
        },
        {
          "text": "Trier les sommets par degré",
          "correct": false,
          "feedback": "Erreur : aucun tri requis.\n"
        },
        {
          "text": "Numéroter les arêtes de $1$ à $m$",
          "correct": false,
          "feedback": "Erreur : c'est plutôt une numérotation, pas une\nliste d'adjacence.\n"
        },
        {
          "text": "Stocker pour chaque sommet la liste de ses voisins",
          "correct": true,
          "feedback": "Bonne réponse : pour le sommet $v$, on stocke la\nliste de tous les sommets directement reliés à\nlui. Plus efficace en mémoire que la matrice\nd'adjacence pour les graphes **creux**\n(peu d'arêtes par sommet). Espace : $O(n + m)$\navec $m$ arêtes.\n"
        }
      ],
      "explanation": "Choix entre matrice et liste : matrice pour graphes\n**denses** (peu de zéros) ou si on a besoin de\ntester très souvent l'existence d'une arête. Liste\npour graphes **creux** (cas le plus fréquent en\npratique)."
    },
    {
      "id": "q06",
      "difficulty": 1,
      "skills": [
        "bfs"
      ],
      "title": "Parcours en largeur",
      "statement": "Le parcours en **largeur** d'un graphe :",
      "options": [
        {
          "text": "Visite les voisins par niveaux successifs",
          "correct": true,
          "feedback": "Bonne réponse : c'est l'algorithme de référence\npour explorer un graphe en respectant la distance\nen nombre d'arêtes. Implémentation : avec une\n**file** (FIFO).\n"
        },
        {
          "text": "Parcourt la branche la plus profonde d'abord",
          "correct": false,
          "feedback": "Erreur : c'est le parcours en **profondeur**\n(DFS), pas le parcours en largeur.\n"
        },
        {
          "text": "Trie les sommets par valeur",
          "correct": false,
          "feedback": "Erreur : aucun tri par valeur.\n"
        },
        {
          "text": "Visite uniquement les feuilles",
          "correct": false,
          "feedback": "Erreur : tous les sommets accessibles sont\nvisités.\n"
        }
      ],
      "explanation": "Application clé : trouver le plus court chemin\n**en nombre d'arêtes** depuis un sommet de départ.\nAussi utilisé pour tester la connexité, calculer\nles composantes connexes, etc."
    },
    {
      "id": "q07",
      "difficulty": 1,
      "skills": [
        "dfs"
      ],
      "title": "Parcours en profondeur",
      "statement": "Le parcours en **profondeur** d'un graphe :",
      "options": [
        {
          "text": "Explore une branche à fond avant de revenir en arrière",
          "correct": true,
          "feedback": "Bonne réponse : implémentation typique avec une\n**pile** (explicite ou implicite via la\nrécursivité). Le parcours en profondeur est\nutile pour la détection de cycles, le tri\ntopologique, la recherche de composantes\nfortement connexes, etc.\n"
        },
        {
          "text": "Trie les sommets par profondeur",
          "correct": false,
          "feedback": "Erreur : aucun tri par profondeur.\n"
        },
        {
          "text": "Visite tous les voisins du sommet de départ avant d'aller plus loin",
          "correct": false,
          "feedback": "Erreur : c'est le parcours en **largeur**.\n"
        },
        {
          "text": "Calcule la longueur totale du graphe",
          "correct": false,
          "feedback": "Erreur : aucune notion de longueur globale.\n"
        }
      ],
      "explanation": "Mémo : **largeur = file = FIFO** ; **profondeur = pile = LIFO**. Les deux parcours visitent tous les\nsommets accessibles, mais dans un ordre différent."
    },
    {
      "id": "q08",
      "difficulty": 1,
      "skills": [
        "structure-bfs"
      ],
      "title": "Structure utilisée par parcours en largeur",
      "statement": "Pour implémenter un parcours en largeur, quelle structure\nde données utilise-t-on naturellement ?",
      "options": [
        {
          "text": "Un dictionnaire",
          "correct": false,
          "feedback": "Erreur : un dictionnaire n'a pas la sémantique\nd'ordre nécessaire.\n"
        },
        {
          "text": "Une pile",
          "correct": false,
          "feedback": "Erreur : la pile correspond au parcours en\n**profondeur**.\n"
        },
        {
          "text": "Une file (FIFO)",
          "correct": true,
          "feedback": "Bonne réponse : on enfile le sommet de départ,\npuis tant que la file n'est pas vide on défile\nun sommet, on visite, et on enfile ses voisins\nnon encore visités. C'est exactement le parcours en largeur.\n"
        },
        {
          "text": "Aucune structure",
          "correct": false,
          "feedback": "Erreur : il faut bien stocker les sommets en\nattente.\n"
        }
      ],
      "explanation": "En Python : `from collections import deque ;\nf = deque([depart])`. Avec `f.append(v)` pour enfiler\net `f.popleft()` pour défiler, en $O(1)$ chacun."
    },
    {
      "id": "q09",
      "difficulty": 1,
      "skills": [
        "structure-dfs"
      ],
      "title": "Structure utilisée par parcours en profondeur",
      "statement": "Le parcours en profondeur peut s'implémenter en utilisant :",
      "options": [
        {
          "text": "Une file uniquement",
          "correct": false,
          "feedback": "Erreur : la file donne un parcours en largeur,\npas en profondeur.\n"
        },
        {
          "text": "Un graphe profondément imbriqué",
          "correct": false,
          "feedback": "Cette description ne\ncorrespond pas à une\nstructure standard\nutilisée pour le\nparcours d'un graphe.\n"
        },
        {
          "text": "Un arbre binaire",
          "correct": false,
          "feedback": "Erreur : un arbre est une structure différente,\npas l'outil de parcours.\n"
        },
        {
          "text": "Une pile (ou la récursivité)",
          "correct": true,
          "feedback": "Bonne réponse : la récursivité utilise\nimplicitement la pile d'appels. La version\nitérative utilise explicitement une pile (`list`\navec `append` et `pop`).\n"
        }
      ],
      "explanation": "Récursivité = pile implicite. Itératif avec pile =\npile explicite. Les deux donnent le même résultat\n(à l'ordre des voisins près). La version récursive\nest souvent plus claire."
    },
    {
      "id": "q10",
      "difficulty": 1,
      "skills": [
        "chemin"
      ],
      "title": "Plus court chemin",
      "statement": "Pour trouver le plus court chemin (en nombre d'arêtes)\nentre deux sommets dans un graphe **non pondéré**, on\nutilise :",
      "options": [
        {
          "text": "Un tri topologique",
          "correct": false,
          "feedback": "Erreur : c'est utile pour les DAG, pas pour les\nplus courts chemins.\n"
        },
        {
          "text": "Un parcours en largeur",
          "correct": true,
          "feedback": "Bonne réponse : le parcours en largeur visite les sommets par\nordre croissant de distance (en arêtes) depuis\nle sommet de départ. Le premier moment où on\natteint le sommet d'arrivée donne la distance\nminimale.\n"
        },
        {
          "text": "Un parcours en profondeur",
          "correct": false,
          "feedback": "Erreur : parcours en profondeur ne donne pas le plus court chemin\nen général. Il peut prendre une route longue.\n"
        },
        {
          "text": "L'algorithme de Dijkstra",
          "correct": false,
          "feedback": "Erreur : Dijkstra est pour les graphes\n**pondérés** (poids positifs). Pour un graphe non\npondéré, parcours en largeur suffit.\n"
        }
      ],
      "explanation": "Pour graphe pondéré à poids **positifs** : Dijkstra\n($O((n + m) \\log n)$). Pour graphe avec poids\nnégatifs : Bellman-Ford ($O(nm)$). parcours en largeur s'utilise\nquand toutes les arêtes ont un poids identique\n(typiquement, $1$)."
    },
    {
      "id": "q11",
      "difficulty": 2,
      "skills": [
        "degre"
      ],
      "title": "Degré d'un sommet",
      "statement": "Le **degré** d'un sommet dans un graphe non orienté\nest :",
      "options": [
        {
          "text": "Le nombre d'arêtes incidentes à ce sommet",
          "correct": true,
          "feedback": "Bonne réponse : c'est aussi le nombre de ses voisins\ndans un graphe simple. Par exemple, dans le graphe\n$\\{(A, B), (A, C), (B, C)\\}$, le sommet $A$ est\nde degré $2$. Pour un graphe orienté, on\ndistingue le **degré entrant** et le **degré sortant**.\n"
        },
        {
          "text": "Le poids de l'arête la plus lourde incidente au sommet",
          "correct": false,
          "feedback": "Le degré d'un sommet\nest un nombre d'arêtes,\net non un poids. Le\npoids n'intervient que\ndans les graphes\npondérés, et n'est pas\nlié à la notion de\ndegré.\n"
        },
        {
          "text": "La distance de ce sommet à la racine du graphe",
          "correct": false,
          "feedback": "Erreur : il n'y a pas de racine dans un graphe\ngénéral, et une distance se mesure entre deux\nsommets. Confusion avec les arbres.\n"
        },
        {
          "text": "Sa profondeur dans un parcours en largeur",
          "correct": false,
          "feedback": "Erreur : la profondeur dépend du parcours choisi\net de son sommet de départ, alors que le degré\nest une propriété du sommet lui-même.\n"
        }
      ],
      "explanation": "Théorème classique (Euler) : la somme des degrés\nd'un graphe non orienté est $2m$ (deux fois le\nnombre d'arêtes), car chaque arête contribue $1$ à\ndeux sommets."
    },
    {
      "id": "q12",
      "difficulty": 2,
      "skills": [
        "chemin-cycle"
      ],
      "title": "Cycle dans un graphe",
      "statement": "Un **cycle** dans un graphe est :",
      "options": [
        {
          "text": "Un sommet isolé",
          "correct": false,
          "feedback": "Erreur : un sommet isolé n'a aucune arête. Ce\nn'est pas un cycle.\n"
        },
        {
          "text": "Une seule arête répétée plusieurs fois de suite",
          "correct": false,
          "feedback": "Erreur : c'est un multi-graphe, notion\ndifférente.\n"
        },
        {
          "text": "Un sommet de degré pair",
          "correct": false,
          "feedback": "Erreur : pas la définition d'un cycle.\n"
        },
        {
          "text": "Une suite d'arêtes qui revient au départ",
          "correct": true,
          "feedback": "Bonne réponse : par exemple, le chemin\n$A \\to B \\to C \\to A$ forme un cycle. Détecter\nles cycles est important : un graphe sans cycle\nest appelé **acyclique** (DAG si orienté).\n"
        }
      ],
      "explanation": "Un **arbre** est un graphe connexe sans cycle. C'est\ncette absence de cycle qui caractérise l'arbre par\nrapport au graphe général."
    },
    {
      "id": "q13",
      "difficulty": 2,
      "skills": [
        "connexite"
      ],
      "title": "Graphe connexe",
      "statement": "Un graphe non orienté est **connexe** quand :",
      "options": [
        {
          "text": "Il est orienté",
          "correct": false,
          "feedback": "Erreur : la connexité concerne les graphes\norientés ET non orientés.\n"
        },
        {
          "text": "Il a beaucoup d'arêtes",
          "correct": false,
          "feedback": "Erreur : la quantité ne suffit pas. C'est la\nstructure qui compte.\n"
        },
        {
          "text": "Un chemin relie toute paire de sommets",
          "correct": true,
          "feedback": "Bonne réponse : on peut « atteindre » n'importe\nquel sommet depuis n'importe quel autre. Si on\nenlève cette propriété, on peut avoir plusieurs\n**composantes connexes** (groupes isolés).\n"
        },
        {
          "text": "Il possède au moins $n$ arêtes au total",
          "correct": false,
          "feedback": "Erreur : un graphe connexe à $n$ sommets a au\nmoins $n - 1$ arêtes (cas de l'arbre), mais\ncela n'est pas suffisant.\n"
        }
      ],
      "explanation": "Le parcours en largeur ou parcours en profondeur depuis un sommet visite toute sa\n**composante connexe**. Si on n'atteint pas tous les\nsommets, le graphe n'est pas connexe."
    },
    {
      "id": "q15",
      "difficulty": 2,
      "skills": [
        "taille-matrice"
      ],
      "title": "Espace mémoire matrice vs liste",
      "statement": "Pour un graphe à $n = 1000$ sommets et $m = 5000$\narêtes, quelle représentation est la plus économe en\nmémoire ?",
      "options": [
        {
          "text": "La liste d'adjacence ($O(n+m)$)",
          "correct": true,
          "feedback": "Bonne réponse : la liste d'adjacence consomme\nde l'ordre du nombre d'arêtes plus le nombre de\nsommets. Beaucoup mieux que la matrice quand le\ngraphe est **creux** ($m \\ll n^2$), ce qui est\nle cas en pratique pour beaucoup d'applications.\n"
        },
        {
          "text": "La matrice d'adjacence ($O(n^2) = 10^6$ cases)",
          "correct": false,
          "feedback": "Erreur : la matrice consomme bien plus que la\nliste pour un graphe creux. La liste vaut mieux.\n"
        },
        {
          "text": "Aucune différence vraiment notable",
          "correct": false,
          "feedback": "Erreur : la différence est importante, surtout\npour les graphes creux.\n"
        },
        {
          "text": "Cela dépend du langage de programmation",
          "correct": false,
          "feedback": "Erreur : la complexité asymptotique est\nindépendante du langage.\n"
        }
      ],
      "explanation": "Densité d'un graphe : $d = m / \\binom{n}{2}$. Pour\n$d$ proche de $1$ (graphe dense), la matrice peut\nêtre préférable. Pour $d$ proche de $0$ (creux), la\nliste est meilleure."
    },
    {
      "id": "q16",
      "difficulty": 2,
      "skills": [
        "bfs-distance"
      ],
      "title": "Parcours en largeur et distance",
      "statement": "Avec un parcours en largeur depuis le sommet $s$, on calcule pour chaque\nsommet $v$ atteignable :",
      "options": [
        {
          "text": "Les sommets isolés",
          "correct": false,
          "feedback": "Erreur : parcours en largeur visite seulement les sommets\naccessibles depuis $s$.\n"
        },
        {
          "text": "Tous les chemins possibles",
          "correct": false,
          "feedback": "Erreur : trop d'information. parcours en largeur calcule\nuniquement la distance minimale, pas tous les\nchemins.\n"
        },
        {
          "text": "Le poids minimal du chemin $s \\to v$ (si pondéré)",
          "correct": false,
          "feedback": "Erreur : parcours en largeur calcule la distance en **nombre d'arêtes**, pas en poids. Pour les poids,\nutiliser Dijkstra.\n"
        },
        {
          "text": "Le nombre minimal d'arêtes du chemin $s \\to v$",
          "correct": true,
          "feedback": "Bonne réponse : c'est précisément ce que parcours en largeur\ncalcule. Très utile dans des graphes où toutes\nles arêtes sont équivalentes (réseau social,\njeu de plateau, etc.).\n"
        }
      ],
      "explanation": "Au moment où parcours en largeur découvre un sommet, il peut\nenregistrer (a) la distance, (b) le sommet\n« parent » dans le parcours en largeur. Avec cela, on peut\nreconstituer le chemin le plus court en arêtes."
    },
    {
      "id": "q17",
      "difficulty": 2,
      "skills": [
        "reseau-social"
      ],
      "title": "Application réseau social",
      "statement": "Sur un réseau social, on veut trouver tous les amis\n« à distance $2$ » (amis d'amis) d'un utilisateur $u$.\nQuel algorithme utiliser ?",
      "options": [
        {
          "text": "Un parcours en largeur limité à la profondeur $2$",
          "correct": true,
          "feedback": "Bonne réponse : parcours en largeur avec une limite de\nprofondeur. À profondeur $0$ : $u$ ; à $1$ : ses\namis ; à $2$ : les amis des amis (sans compter\n$u$ ni ses amis directs si on veut). Très\nutilisé pour la recommandation d'amis.\n"
        },
        {
          "text": "Un tri des amis par valeur d'identifiant",
          "correct": false,
          "feedback": "Le tri d'une liste n'a\naucun rapport avec la\nrecherche d'amis à une\ndistance donnée dans un\nréseau social.\n"
        },
        {
          "text": "Un parcours en profondeur sans limite",
          "correct": false,
          "feedback": "Erreur : parcours en profondeur n'organise pas les visites par\nniveaux ; difficile d'extraire « distance $2$ ».\n"
        },
        {
          "text": "Une recherche dichotomique",
          "correct": false,
          "feedback": "Erreur : la dichotomie suppose une structure\nordonnée, ce qui n'est pas le cas d'un graphe\nsocial.\n"
        }
      ],
      "explanation": "Cette technique est utilisée par LinkedIn, Facebook,\netc. La théorie des « six degrés de séparation »\naffirme que dans le graphe social mondial, la\ndistance maximale est environ $6$."
    },
    {
      "id": "q18",
      "difficulty": 2,
      "skills": [
        "implementation-dictionnaire"
      ],
      "title": "Liste d'adjacence en Python",
      "statement": "Comment représenter naturellement une liste d'adjacence\nen Python ?",
      "options": [
        {
          "text": "Comme un entier codant le nombre total d'arêtes",
          "correct": false,
          "feedback": "Erreur : un entier ne résume qu'une quantité. Il ne\ndit pas quels sommets sont reliés entre eux.\n"
        },
        {
          "text": "Comme un tuple unique contenant tous les sommets",
          "correct": false,
          "feedback": "Erreur : on saurait quels sommets existent, mais pas\nlesquels sont voisins. Un tuple, immuable, ne permet\npas non plus d'ajouter une arête.\n"
        },
        {
          "text": "Comme une chaîne de caractères énumérant les arêtes",
          "correct": false,
          "feedback": "Erreur : il faudrait analyser le texte à chaque\naccès aux voisins. Difficile à manipuler et\ninefficace.\n"
        },
        {
          "text": "Comme un dictionnaire sommet vers liste de voisins",
          "correct": true,
          "feedback": "Bonne réponse : par exemple,\n`g = {'A': ['B', 'C'], 'B': ['A'], 'C': ['A']}`.\nTrès lisible et efficace. On accède aux voisins\nen $O(1)$ via la clé.\n"
        }
      ],
      "explanation": "Variante orientée objet : créer une classe `Graphe`\navec méthodes `ajouter_sommet`, `ajouter_arete`,\n`voisins`, etc. Plus propre pour des projets\nstructurés. Mais en NSI, le dictionnaire suffit\nsouvent."
    },
    {
      "id": "q19",
      "difficulty": 2,
      "skills": [
        "parcours-implementation"
      ],
      "title": "Code parcours en largeur",
      "statement": "Quelle fonction Python implémente correctement un parcours en largeur\ndepuis un sommet `depart` dans un graphe `g` (dictionnaire) ?",
      "options": [
        {
          "text": "```\ndef bfs(g, depart):\n    visites = {depart}\n    pile = [depart]\n    while pile:\n        v = pile.pop()\n        for w in g[v]:\n            if w not in visites:\n                visites.add(w)\n                pile.append(w)\n    return visites\n```\n",
          "correct": false,
          "feedback": "Erreur : `pile.pop()` retire le **dernier**\nélément ajouté : c'est une pile (LIFO), donc\nun parcours en **profondeur**, pas en\nlargeur. Le parcours en largeur exige une\nfile (FIFO), par exemple `deque` avec\n`popleft`.\n"
        },
        {
          "text": "```\ndef bfs(g, depart):\n    visites = {depart}\n    from collections import deque\n    f = deque([depart])\n    while f:\n        v = f.popleft()\n        for w in g[v]:\n            if w not in visites:\n                visites.add(w)\n                f.append(w)\n    return visites\n```\n",
          "correct": true,
          "feedback": "Bonne réponse : structure standard. On garde une\nensemble `visites` pour éviter les boucles\ninfinies sur les graphes cycliques. La file\nimpose le parcours en largeur.\n"
        },
        {
          "text": "```\ndef bfs(g, depart):\n    return g[depart]\n```\n",
          "correct": false,
          "feedback": "Erreur : retourne uniquement les voisins\ndirects, pas le parcours complet.\n"
        },
        {
          "text": "```\ndef bfs(g, depart):\n    from collections import deque\n    f = deque([depart])\n    resultat = []\n    while f:\n        v = f.popleft()\n        resultat.append(v)\n        for w in g[v]:\n            f.append(w)\n    return resultat\n```\n",
          "correct": false,
          "feedback": "Erreur : sans ensemble `visites`, chaque\nsommet est réenfilé à chaque fois qu'on le\nrencontre : sur un graphe avec cycle, la\nboucle ne termine jamais. Le marquage des\nsommets visités est indispensable.\n"
        }
      ],
      "explanation": "L'ensemble `visites` est crucial : sans lui, sur un\ngraphe avec cycle, on bouclerait indéfiniment.\nC'est l'une des erreurs classiques sur les graphes."
    },
    {
      "id": "q20",
      "difficulty": 2,
      "skills": [
        "arbre-vs-graphe"
      ],
      "title": "Arbre comme cas particulier",
      "statement": "Un **arbre** (au sens informatique) est un cas\nparticulier de :",
      "options": [
        {
          "text": "Un graphe connexe acyclique",
          "correct": true,
          "feedback": "Bonne réponse : tous les concepts d'arbre\n(racine, hauteur, parcours) sont applicables aux\ngraphes plus généralement, mais l'arbre garantit\ndes propriétés (chemin unique entre deux\nsommets) qui simplifient les algorithmes.\n"
        },
        {
          "text": "Un tableau linéaire à une dimension",
          "correct": false,
          "feedback": "Erreur : un tableau est linéaire, un arbre\nhiérarchique.\n"
        },
        {
          "text": "Une file (premier entré, premier sorti)",
          "correct": false,
          "feedback": "Erreur : une file est une structure linéaire,\nsans branchement. File et arbre sont distincts.\n"
        },
        {
          "text": "Une pile (dernier entré, premier sorti)",
          "correct": false,
          "feedback": "Erreur : une pile est linéaire elle aussi. Pile\net arbre sont des structures totalement\ndifférentes.\n"
        }
      ],
      "explanation": "Tout arbre à $n$ sommets a exactement $n - 1$\narêtes. Si l'on a $n$ sommets, $n - 1$ arêtes et la\nconnexité, alors c'est un arbre (et donc sans\ncycle). Triple caractérisation."
    },
    {
      "id": "q21",
      "difficulty": 3,
      "skills": [
        "dijkstra"
      ],
      "title": "Algorithme de Dijkstra",
      "statement": "L'algorithme de **Dijkstra** calcule les plus courts\nchemins depuis une source dans un graphe pondéré. Quelle\nest sa contrainte sur les poids ?",
      "options": [
        {
          "text": "Les poids doivent être positifs ou nuls",
          "correct": true,
          "feedback": "Bonne réponse : avec des poids négatifs,\nl'invariant glouton de Dijkstra ne tient plus.\nPour les poids négatifs, utiliser Bellman-Ford.\n"
        },
        {
          "text": "Les poids doivent être entiers",
          "correct": false,
          "feedback": "Erreur : Dijkstra fonctionne avec des poids\nréels.\n"
        },
        {
          "text": "Les poids doivent être triés à l'avance",
          "correct": false,
          "feedback": "Erreur : aucun tri préalable requis. Dijkstra\ngère lui-même la priorité via une file de\npriorité.\n"
        },
        {
          "text": "Les poids doivent être tous identiques",
          "correct": false,
          "feedback": "Erreur : si tous les poids sont identiques, parcours en largeur\nsuffit. Dijkstra est conçu pour des poids\nvariables.\n"
        }
      ],
      "explanation": "Dijkstra s'implémente classiquement avec une **file de priorité** (tas), donnant une complexité\n$O((n + m) \\log n)$. Très utilisé dans les GPS,\nles protocoles de routage et les jeux vidéo\n(recherche de chemin pour les déplacements de\npersonnages)."
    },
    {
      "id": "q25",
      "difficulty": 3,
      "skills": [
        "synthese"
      ],
      "title": "Choisir le bon algorithme",
      "statement": "Vous voulez calculer le chemin le plus court entre deux\nvilles sur un réseau routier (les routes ont des\nlongueurs en kilomètres positives). Quel algorithme\nchoisir ?",
      "options": [
        {
          "text": "Un parcours en profondeur",
          "correct": false,
          "feedback": "Erreur : parcours en profondeur ne calcule pas les plus courts\nchemins.\n"
        },
        {
          "text": "Un parcours en largeur",
          "correct": false,
          "feedback": "Erreur : parcours en largeur minimise le **nombre d'arêtes**, pas\nla **somme des poids**. Le résultat ignorerait\nles longueurs.\n"
        },
        {
          "text": "Tri topologique",
          "correct": false,
          "feedback": "Erreur : utile pour les DAG, pas pour les\nplus courts chemins en général.\n"
        },
        {
          "text": "L'algorithme de Dijkstra",
          "correct": true,
          "feedback": "Bonne réponse : Dijkstra est exactement conçu\npour ce cas. Pour des graphes très grands, on\nutilise des variantes optimisées (A*, Dijkstra\nbidirectionnel, contractions).\n"
        }
      ],
      "explanation": "Récapitulatif algorithmes plus courts chemins :\nparcours en largeur (non pondéré), Dijkstra (poids positifs),\nBellman-Ford (poids négatifs sans cycle négatif),\nFloyd-Warshall (toutes paires de sommets en\n$O(n^3)$)."
    },
    {
      "id": "q26",
      "difficulty": 2,
      "skills": [
        "conversion-representation"
      ],
      "title": "Conversion entre représentations",
      "statement": "Soit le graphe non orienté représenté par la\nliste d'adjacence\n`g = {'A': ['B', 'C'], 'B': ['A', 'C'], 'C': ['A', 'B']}`.\nQuelle est la matrice d'adjacence correspondante\navec l'ordre des sommets `[A, B, C]` ?",
      "options": [
        {
          "text": "```\n[[0, 1, 0],\n [1, 0, 1],\n [0, 1, 0]]\n```\n",
          "correct": false,
          "feedback": "Erreur : cette matrice correspondrait à un\ngraphe en chaîne `A - B - C`, où `A` et `C`\nne sont pas directement reliés. Or le graphe\ndonné est complet : `A` et `C` sont voisins.\n"
        },
        {
          "text": "```\n[[0, 1, 1],\n [1, 0, 1],\n [1, 1, 0]]\n```\n",
          "correct": true,
          "feedback": "Bonne réponse : chaque sommet est relié à\nchacun des deux autres (graphe complet $K_3$).\nLa diagonale est nulle (pas de boucle), et la\nmatrice est symétrique (graphe non orienté).\nToutes les autres cases valent $1$ car les\narêtes existent dans tous les sens.\n"
        },
        {
          "text": "```\n[[0, 0, 0],\n [0, 0, 0],\n [0, 0, 0]]\n```\n",
          "correct": false,
          "feedback": "Erreur : ce serait un graphe sans aucune\narête. Or le graphe possède bien des arêtes\n(chaque sommet a des voisins dans la liste\nd'adjacence).\n"
        },
        {
          "text": "```\n[[1, 1, 1],\n [1, 1, 1],\n [1, 1, 1]]\n```\n",
          "correct": false,
          "feedback": "Erreur : la diagonale ne doit pas être à $1$\ncar aucun sommet n'a de boucle sur lui-même\n(par exemple, `g['A']` ne contient pas `'A'`).\n"
        }
      ],
      "explanation": "Pour un graphe non orienté, la matrice\nd'adjacence est toujours **symétrique**. Pour un\ngraphe orienté, ce n'est pas nécessairement le\ncas. Les deux représentations sont équivalentes\nen information ; le choix dépend des opérations\nles plus fréquentes (test d'arête en O(1) avec\nla matrice ; énumération des voisins efficace\navec la liste)."
    },
    {
      "id": "q28",
      "difficulty": 2,
      "skills": [
        "trace-bfs"
      ],
      "title": "Trace d'un parcours en largeur",
      "statement": "On effectue un parcours en largeur du graphe\n`g = {'A': ['B', 'C'], 'B': ['A', 'D'], 'C': ['A', 'D'], 'D': ['B', 'C', 'E'], 'E': ['D']}`\nen partant de `'A'`, en visitant les voisins\ndans l'ordre où ils apparaissent dans la liste.\nDans quel ordre les sommets sont-ils visités ?",
      "options": [
        {
          "text": "A, B, C, D, E\n",
          "correct": true,
          "feedback": "Bonne réponse : on commence par enfiler `A`,\npuis on défile `A` et on enfile ses voisins\n`B` et `C`. On défile `B`, on enfile `D`\n(`A` est déjà visité). On défile `C` (ses\nvoisins `A` et `D` sont déjà visités ou\nenfilés). On défile `D`, on enfile `E`. On\ndéfile `E`. Ordre de visite : A, B, C, D, E.\n"
        },
        {
          "text": "A, B, D, E, C\n",
          "correct": false,
          "feedback": "Erreur : c'est un parcours en **profondeur**\n(qui suit une branche en suivant les\nvoisins). Le parcours en largeur, lui,\nvisite les sommets par niveaux croissants\nde distance, pas en plongeant.\n"
        },
        {
          "text": "A, C, B, D, E\n",
          "correct": false,
          "feedback": "Erreur : on visite les voisins dans l'ordre\noù ils apparaissent dans la liste. Pour `A`,\nc'est `B` puis `C`. Donc `B` est visité\navant `C`, pas l'inverse.\n"
        },
        {
          "text": "A, B, C, E, D\n",
          "correct": false,
          "feedback": "Erreur : on visite `D` avant `E` car `D` est\nenfilé en premier (par `B`), et `E` n'est\nenfilé que plus tard (quand on visite `D`).\nL'ordre est donc D avant E.\n"
        }
      ],
      "explanation": "Le parcours en largeur visite les sommets dans\nl'ordre croissant de leur distance (en arêtes)\nau sommet de départ. Niveau $0$ : $A$. Niveau\n$1$ : $B$, $C$. Niveau $2$ : $D$. Niveau $3$ :\n$E$. C'est exactement ce qui justifie l'usage\ndu parcours en largeur pour calculer les plus\ncourts chemins en nombre d'arêtes."
    },
    {
      "id": "q29",
      "difficulty": 3,
      "skills": [
        "trace-dijkstra"
      ],
      "title": "Trace de l'algorithme de Dijkstra",
      "statement": "On considère le graphe pondéré suivant\n(arêtes non orientées) :\n`A-B (4)`, `A-C (2)`, `B-C (1)`, `B-D (5)`,\n`C-D (8)`, `C-E (10)`, `D-E (2)`. On lance\nDijkstra depuis le sommet `A`. Quelle est la\ndistance minimale de `A` à `E` ?",
      "options": [
        {
          "text": "$14$ (en passant par `A-B-D-E`)",
          "correct": false,
          "feedback": "Erreur : ce chemin pèse $4 + 5 + 2 = 11$,\npas $14$. Et il n'est pas optimal :\npasser d'abord par `C` permet d'atteindre\n`B` à coût $3$ (au lieu de $4$), ce qui\nréduit ensuite le chemin total.\n"
        },
        {
          "text": "$12$ (en passant directement par `C-E`)",
          "correct": false,
          "feedback": "Erreur : c'est une distance valide\n(chemin `A → C → E`), mais ce n'est pas\nla **plus courte**. Le chemin\n`A → C → B → D → E` totalise\n$2 + 1 + 5 + 2 = 10$, ce qui est moins.\nDijkstra trouve toujours le minimum.\n"
        },
        {
          "text": "$11$",
          "correct": false,
          "feedback": "Erreur : le chemin `A → B → D → E` pèse bien\n$4 + 5 + 2 = 11$, mais ce n'est pas le plus\ncourt. En considérant les autres chemins,\n`A → C → E` pèse $12$ et\n`A → C → B → D → E` pèse\n$2 + 1 + 5 + 2 = 10$. C'est ce dernier\nqui est minimal.\n"
        },
        {
          "text": "$10$",
          "correct": true,
          "feedback": "Bonne réponse : on trace les distances\nsuccessivement. Étape 1 : on extrait\n`A` ($0$), on met à jour `B=4`, `C=2`.\nÉtape 2 : on extrait `C` (le plus\npetit, $2$), on met à jour\n`B = min(4, 2+1) = 3`, `D = 2+8 = 10`,\n`E = 2+10 = 12`. Étape 3 : on extrait\n`B` ($3$), on met à jour\n`D = min(10, 3+5) = 8`. Étape 4 : on\nextrait `D` ($8$), on met à jour\n`E = min(12, 8+2) = 10`. Étape 5 : on\nextrait `E` ($10$), parcours terminé.\nLe chemin optimal est donc\n`A → C → B → D → E` de poids total\n$2 + 1 + 5 + 2 = 10$.\n"
        }
      ],
      "explanation": "Méthode systématique : tenir un tableau\n`dist[v]` pour chaque sommet. Tant qu'il\nreste des sommets non traités, extraire le\nsommet de plus petite distance, et relâcher\n(mettre à jour) ses voisins :\n`dist[w] = min(dist[w], dist[v] + poids(v,w))`.\nCet algorithme est **glouton** : on traite\ndéfinitivement un sommet quand on l'extrait.\nCela exige des poids positifs ; sinon,\nutiliser Bellman-Ford."
    },
    {
      "id": "q30",
      "difficulty": 2,
      "skills": [
        "code-dfs-recursif"
      ],
      "title": "Code du parcours en profondeur récursif",
      "statement": "Quel code Python implémente correctement un\nparcours en profondeur récursif d'un graphe `g`\n(dictionnaire d'adjacence) à partir d'un\nsommet `depart` ?",
      "options": [
        {
          "text": "```python\ndef parcours_profondeur(g, depart, visites=None):\n    if visites is None:\n        visites = []\n    visites.append(depart)\n    for voisin in g[depart]:\n        if voisin not in visites:\n            parcours_profondeur(g, voisin, visites)\n    return visites\n```\n",
          "correct": true,
          "feedback": "Bonne réponse : on marque le sommet\ncourant en l'ajoutant à `visites`, puis\non appelle récursivement la fonction sur\nchaque voisin non encore visité. La\nrécursivité utilise implicitement la\npile d'appels, ce qui réalise\nnaturellement le LIFO du parcours en\nprofondeur. L'astuce `visites=None` puis\ncréation d'une liste vide dans la\nfonction évite le piège du paramètre par\ndéfaut mutable, qui serait partagé entre\nles appels successifs. Sur le graphe\n`g = {\"A\": [\"B\", \"C\"], \"B\": [\"A\", \"D\"], \"C\": [\"A\", \"D\"], \"D\": [\"B\", \"C\"]}`,\nl'appel `parcours_profondeur(g, \"A\")`\nrenvoie `['A', 'B', 'D', 'C']`.\n"
        },
        {
          "text": "```python\ndef parcours_profondeur(g, depart):\n    for voisin in g[depart]:\n        parcours_profondeur(g, voisin)\n```\n",
          "correct": false,
          "feedback": "Erreur : aucune trace des sommets\nvisités. Sur un graphe avec un cycle, la\nfonction boucle indéfiniment et finit\npar lever `RecursionError`. La\nmémorisation des sommets déjà visités\nest essentielle.\n"
        },
        {
          "text": "```python\ndef parcours_profondeur(g, depart, visites=None):\n    if visites is None:\n        visites = []\n    visites.append(depart)\n    for voisin in g[depart]:\n        if voisin not in visites:\n            parcours_profondeur(g, voisin)\n    return visites\n```\n",
          "correct": false,
          "feedback": "Erreur : l'appel récursif ne transmet pas\n`visites`, donc chaque appel repart d'une\nliste vide et oublie les sommets déjà\nvus. Même sur un graphe sans cycle, la\nliste renvoyée ne contiendrait que le\nsommet de départ ; et dès qu'un sommet\nest atteignable par deux chemins (toute\narête non orientée suffit), la fonction\nrepasse sans fin par les mêmes sommets et\nl'appel finit sur `RecursionError`.\n"
        },
        {
          "text": "```python\ndef parcours_profondeur(g, depart, visites={}):\n    visites[depart] = True\n    for voisin in g[depart]:\n        if voisin not in visites:\n            parcours_profondeur(g, voisin, visites)\n    return visites\n```\n",
          "correct": false,
          "feedback": "Erreur : piège classique du paramètre par\ndéfaut mutable. Le dictionnaire `visites = {}`\nest créé **une seule fois**, à la\ndéfinition de la fonction. Tous les\nappels successifs à\n`parcours_profondeur(g, ...)` partagent\nalors le même dictionnaire, ce qui\nconduit à un bug subtil : un nouveau\nparcours hérite des sommets visités par\nles parcours antérieurs. Après un premier\nparcours ayant visité `A`, `B`, `C` et\n`D`, un appel sur un autre graphe\n`{\"X\": [\"Y\"], \"Y\": [\"X\"]}` renvoie un\ndictionnaire qui contient encore `A`,\n`B`, `C` et `D` en plus de `X` et `Y`.\n"
        }
      ],
      "explanation": "Pour transformer ce parcours en profondeur\nrécursif en parcours en profondeur itératif\navec pile explicite, on remplace la\nrécursion par une pile que l'on dépile et\nempile manuellement. Cela permet de mieux\ncontrôler la consommation de pile et\nd'éviter `RecursionError` sur les très\ngrands graphes."
    },
    {
      "id": "q22",
      "difficulty": 3,
      "skills": [
        "synthese",
        "parcours-profondeur",
        "trace"
      ],
      "title": "Trace d'un parcours en profondeur",
      "statement": "On effectue un parcours en profondeur récursif depuis\nle sommet $A$ sur le graphe non orienté suivant :\n\n- $A$ est relié à $B$ et $C$ ;\n- $B$ est relié à $D$ ;\n- $C$ est relié à $D$ et $E$ ;\n- $D$ est relié à $E$.\n\nOn suppose que, à chaque sommet visité, les voisins sont\nexaminés dans l'ordre alphabétique. Dans quel ordre les\nsommets sont-ils découverts ?",
      "options": [
        {
          "text": "$A, B, D, C, E$",
          "correct": true,
          "feedback": "Bonne réponse : on s'enfonce d'abord. Depuis $A$,\non visite $B$ (premier voisin alphabétique), puis\n$D$ (seul voisin non vu de $B$). Depuis $D$, les\nvoisins non vus sont $C$ et $E$ : l'alphabet impose\n$C$, dont le seul voisin non vu est $E$.\n"
        },
        {
          "text": "$A, B, C, D, E$",
          "correct": false,
          "feedback": "Erreur : c'est la trace du parcours en **largeur**\n(par couches : $B$ et $C$ voisins de $A$, puis $D$,\npuis $E$). En profondeur, on descend via $B$ puis\n$D$ avant de revenir à $C$.\n"
        },
        {
          "text": "$A, B, D, E, C$",
          "correct": false,
          "feedback": "Erreur : depuis $D$, les voisins non visités sont\n$C$ et $E$ ; l'ordre alphabétique impose de\ndescendre par $C$ avant $E$.\n"
        },
        {
          "text": "$A, C, D, B, E$",
          "correct": false,
          "feedback": "Erreur : trace d'un parcours en profondeur qui\nexaminerait $C$ avant $B$ ; or l'ordre alphabétique\nimpose $B$ comme premier voisin de $A$.\n"
        }
      ],
      "explanation": "Le parcours en profondeur (DFS) s'enfonce le long d'une\nbranche avant de revenir en arrière (pile ou récursion),\nlà où le parcours en largeur (file) progresse par\ncouches : sur ce même graphe, la largeur donnerait\n$A, B, C, D, E$. Comparer les deux traces sur un même\ngraphe est le meilleur moyen de les distinguer."
    },
    {
      "id": "q23",
      "difficulty": 3,
      "skills": [
        "synthese",
        "representation",
        "complexite"
      ],
      "title": "Choisir entre matrice et liste d'adjacence",
      "statement": "On souhaite stocker un graphe non orienté représentant\nun réseau social : environ un million d'utilisateurs\n(sommets) et en moyenne une centaine de contacts par\npersonne (graphe creux, environ $10^8$ arêtes au total).\nQuelle représentation est la plus adaptée en pratique ?",
      "options": [
        {
          "text": "Une liste d'adjacence, la matrice serait bien trop énorme",
          "correct": true,
          "feedback": "Bonne réponse : une matrice d'adjacence à $10^6$\nsommets occupe $10^{12}$ cases, soit environ un\ntéraoctet pour un seul bit par case, ce qui est\ninenvisageable. Une liste d'adjacence ne stocke que\nles arêtes réellement présentes (environ $10^8$),\nce qui reste dans des ordres de grandeur\nmanipulables.\n"
        },
        {
          "text": "Une matrice d'adjacence, car elle permet de tester l'existence d'une arête en temps constant",
          "correct": false,
          "feedback": "Erreur : le temps constant pour tester l'existence\nd'une arête est un avantage théorique réel, mais la\ntaille de la matrice ($10^{12}$ cases) la rend\ninutilisable sur un graphe d'un million de sommets.\n"
        },
        {
          "text": "Une matrice d'adjacence, car elle est plus simple à implémenter et à comprendre",
          "correct": false,
          "feedback": "Erreur : la simplicité d'implémentation ne compense\npas un coût mémoire prohibitif. Le critère décisif\ndans ce scénario est la consommation mémoire.\n"
        },
        {
          "text": "Aucune des deux représentations n'est utilisable à cette taille de graphe",
          "correct": false,
          "feedback": "Erreur : la liste d'adjacence est exactement la\nstructure que tous les moteurs de réseaux sociaux\nutilisent en pratique. Elle tient parfaitement en\nmémoire pour un graphe creux à un million de\nsommets.\n"
        }
      ],
      "explanation": "Règle générale : pour un graphe **dense** (beaucoup\nd'arêtes par rapport au carré du nombre de sommets),\nla matrice d'adjacence est compétitive ; pour un\ngraphe **creux**, la liste d'adjacence l'emporte tant\nen mémoire qu'en temps pour la plupart des\nalgorithmes (parcours, plus courts chemins)."
    }
  ]
}