Degrés & lemme des poignées
Soit le graphe non orienté de sommets
Indices (3)
La matrice d'adjacence
Le degré d'un sommet est la somme de sa ligne dans
Lemme des poignées :
Correction détaillée
Un graphe est la structure de base de tout ce chapitre : des sommets reliés par des arêtes. Pour le manipuler par le calcul, il faut le représenter, et la représentation la plus directe est la matrice d'adjacence.
Le degré d'un sommet est son nombre de voisins. Et le lemme des poignées de main relie les deux : il dit que la somme des degrés vaut le double du nombre d'arêtes.
Sommets
Le graphe est non orienté, donc chaque arête
Contrôles de forme, à faire systématiquement :
- la matrice est symétrique ✓ (graphe non orienté) ;
- la diagonale est nulle ✓ (aucune boucle : pas d'arête d'un sommet vers lui-même) ;
- il y a
coefficients égaux à ✓ (deux par arête).
Le degré d'un sommet se lit sur sa ligne : c'est la somme des coefficients.
| sommet | voisins | degré |
|---|---|---|
Le sommet
Vérification :
Pourquoi c'est vrai — et l'argument est joli : chaque arête a deux extrémités, donc elle contribue
Le nom vient de l'interprétation : dans une réunion, la somme des nombres de mains serrées par chaque personne vaut le double du nombre de poignées de main échangées.
Séparons la somme selon la parité des degrés :
La seconde somme est donc paire. Or c'est une somme de nombres impairs : elle n'est paire que s'il y en a un nombre pair.
Vérification ici : les degrés sont
Ce corollaire est loin d'être anecdotique : c'est lui qui décide de l'existence d'un parcours eulérien (exercice A4).
On peut aussi lire les degrés matriciellement : le vecteur des degrés est
Et le nombre d'arêtes vaut
Autre propriété utile : le coefficient
Réponse : la matrice est symétrique à diagonale nulle avec dix
Parcours BFS et DFS
Sur le graphe précédent (arêtes
Indices (3)
BFS : file d'attente, on visite tous les voisins avant d'aller plus loin.
DFS : pile (ou récursion), on plonge le plus loin possible.
Voisins de
Correction détaillée
Explorer un graphe, c'est visiter tous ses sommets à partir d'un point de départ. Deux stratégies, qui diffèrent par une seule structure de données :
| structure | comportement | |
|---|---|---|
| BFS (parcours en largeur) | FILE — premier entré, premier sorti | explore par couches : tous les voisins, puis les voisins des voisins |
| DFS (parcours en profondeur) | PILE — dernier entré, premier sorti | s'enfonce aussi loin que possible, puis revient en arrière |
Le graphe : sommets
On part de
| étape | on traite | on découvre | file après | visités |
|---|---|---|---|---|
| 1 | ||||
| 2 | rien ( |
|||
| 3 | ||||
| 4 | ||||
| 5 | rien |
Le point à ne pas rater, étape 2 : on ne rajoute pas
On part de
| on est en | voisins non visités | on va en |
|---|---|---|
| aucun | retour arrière |
C'est le fait le plus instructif de l'exercice, et il faut savoir l'expliquer.
Sur ce graphe, BFS et DFS visitent les sommets dans le même ordre. Ce n'est pas une erreur, c'est une coïncidence de structure : le graphe est presque un chemin, il n'offre pas de « choix » qui ferait diverger les deux stratégies.
Mais les ARBRES d'exploration diffèrent, et c'est là qu'on voit la différence :
| sommet | père en BFS | père en DFS |
|---|---|---|
Le sommet
| sommet | profondeur BFS | profondeur DFS |
|---|---|---|
Les profondeurs BFS sont les vraies distances en nombre d'arêtes ; les profondeurs DFS ne le sont pas.
C'est la seule chose à retenir vraiment, parce qu'elle décide du choix en pratique.
| BFS | DFS |
|---|---|
| plus courts chemins en nombre d'arêtes | détection de cycles |
| niveaux / couches (réseaux sociaux : « à 2 amis de distance ») | tri topologique (exercice A6) |
| propagation, diffusion | composantes fortement connexes |
| il visite |
il le voit à profondeur |
La propriété fondamentale du BFS : il découvre les sommets par distance croissante à la source. C'est ce qui en fait l'algorithme des plus courts chemins quand toutes les arêtes ont le même poids — et c'est exactement ce que généralise Dijkstra (exercice B1) pour des poids quelconques.
Complexité : les deux sont en
Réponse : BFS et DFS donnent tous deux
breadth_first_order/depth_first_order : Composantes connexes
Le graphe non orienté de sommets
Indices (3)
Une composante connexe est un groupe maximal de sommets reliés entre eux.
Lancer un parcours depuis un sommet : il découvre toute sa composante.
Recommencer depuis un sommet non encore visité.
Correction détaillée
Une composante connexe est un « morceau » du graphe : un ensemble maximal de sommets reliés entre eux par des chemins. Deux sommets de composantes différentes ne peuvent jamais être joints.
La méthode est mécanique : on lance un parcours (BFS ou DFS, exercice A2) depuis un sommet non encore visité, ce qui découvre toute sa composante ; puis on recommence depuis un sommet resté non visité, et ainsi de suite jusqu'à épuisement.
Sommets
Listes de voisins :
⚠️ Comparer avec l'exercice A1 : le graphe est le même, à une arête près — l'arête
Premier parcours, depuis
| on traite | on découvre |
|---|---|
| rien de neuf | |
| rien de neuf |
Composante trouvée :
Deuxième parcours, depuis
| on traite | on découvre |
|---|---|
| rien de neuf |
Composante trouvée :
Tous les sommets sont visités, l'algorithme s'arrête.
| composante | sommets | arêtes | structure |
|---|---|---|---|
| triangle |
|||
| arête simple |
Contrôles :
sommets ✓ — les composantes partitionnent l'ensemble des sommets ; arêtes ✓ — toute arête est à l'intérieur d'une composante, jamais entre deux ; - il n'existe aucun chemin de
à : c'est ce qui définit deux composantes distinctes.
Le nombre
Un graphe à
Deux conséquences immédiates :
| affirmation | vraie ? |
|---|---|
| un graphe connexe ( |
oui |
| un graphe à |
non — c'est notre exemple ! |
Notre graphe est le contre-exemple : il a bien
Le cas d'égalité
La connexité n'est pas une curiosité théorique : c'est souvent la première question à poser sur un réseau.
| domaine | ce que dit une composante |
|---|---|
| réseau informatique | des machines qui ne peuvent pas communiquer |
| réseau social | des groupes sans aucune relation commune |
| routier | des zones inaccessibles l'une depuis l'autre |
| résolution de systèmes | des blocs indépendants, traitables séparément |
Et pour tous les algorithmes du chapitre, la question est préalable : chercher un plus court chemin de
Complexité : un seul parcours global, donc
Réponse :
connected_components : Ponts de Königsberg (Euler)
Le multigraphe des ponts de Königsberg a
Indices (3)
Critère d'Euler : un chemin eulérien existe ssi le graphe connexe a
Compter les sommets de degré impair de Königsberg.
Un circuit fermé exige
Correction détaillée
Königsberg,
Le point remarquable est que la réponse ne demande aucune recherche exhaustive : elle se lit sur les degrés, en une ligne.
Pour un graphe connexe :
| condition sur les degrés | ce qui existe |
|---|---|
| tous pairs | un cycle eulérien (on revient au point de départ) |
| exactement 2 impairs | un chemin eulérien (départ et arrivée = les deux sommets impairs) |
| plus de 2 impairs | rien — ni cycle, ni chemin |
Rappel de l'exercice A1 : le nombre de sommets de degré impair est toujours pair. Les seuls cas possibles sont donc
L'argument est d'une simplicité totale, et il vaut mieux le comprendre que le retenir.
Considérons un sommet intermédiaire du parcours — un sommet qui n'est ni le départ ni l'arrivée. Chaque fois qu'on y entre par un pont, il faut en ressortir par un autre. Les ponts de ce sommet se regroupent donc par paires : son degré est nécessairement pair.
Restent le départ et l'arrivée. Au départ, on sort une fois de plus qu'on n'entre ; à l'arrivée, l'inverse. Ces deux sommets-là peuvent donc être de degré impair.
Si l'on veut un cycle (départ = arrivée), ce sommet-là redevient intermédiaire : tous les degrés doivent être pairs.
Degrés :
Contrôle par le lemme des poignées de main (exercice A1) :
La réponse est donc NON, et Euler l'a établie sans essayer un seul itinéraire — c'est ce qui fait la force de l'argument.
Le cycle à quatre sommets
| sommet | voisins | degré |
|---|---|---|
Et il est évident : le parcours
Contrôle :
Le contraste est net : les deux graphes sont connexes, les deux ont un nombre pair de sommets impairs — mais l'un en a
Reprenons le graphe de l'exercice A1 (arêtes
Il existe donc un chemin eulérien, d'extrémités
Arêtes empruntées :
⚠️ On ne peut pas revenir au départ : il n'y a pas de cycle eulérien, seulement un chemin.
⚠️ Ne pas confondre avec le circuit hamiltonien, qui passe par chaque SOMMET une fois — celui-là n'a aucun critère simple et est NP-difficile (exercice D6). Euler pour les arêtes : facile. Hamilton pour les sommets : difficile. La différence entre les deux problèmes est l'une des plus frappantes du chapitre.
Réponse : NON pour Königsberg — ses quatre sommets de degré impair (
Graphe biparti & cycle impair
Montrer que le cycle
Indices (3)
Biparti
Tenter une
Sur un cycle impair, l'alternance se contredit au bouclage.
Correction détaillée
Un graphe est biparti si l'on peut partager ses sommets en deux groupes tels que toute arête relie un groupe à l'autre — jamais deux sommets du même groupe.
C'est exactement une coloration à 2 couleurs : on peint un groupe en rouge, l'autre en bleu, et aucune arête ne joint deux sommets de même couleur.
Le cycle
La partition :
Vérifions chaque arête — c'est le seul contrôle qui vaille :
| arête | de | vers | traverse ? |
|---|---|---|---|
| ✓ | |||
| ✓ | |||
| ✓ | |||
| ✓ |
Les quatre arêtes traversent
Contrôle de complétude :
Le triangle
Démonstration par l'absurde. Supposons une partition en deux groupes.
est dans un groupe, disons ; - l'arête
force dans l'autre groupe, ; - l'arête
force dans le groupe de , donc ; - mais alors l'arête
relie à : deux sommets du même groupe.
Contradiction
C'est aussi ce qu'on voit en coloriant :
Théorème. Un graphe est biparti si et seulement si il ne contient aucun cycle de longueur impaire.
Pourquoi. En parcourant un cycle, la couleur alterne à chaque arête : rouge, bleu, rouge, bleu… Après
| graphe | cycles | biparti ? | |
|---|---|---|---|
| longueur |
non | ||
| longueur |
oui | ||
| longueur |
non | ||
| longueur |
oui | ||
| arbre (au moins une arête) | aucun cycle | toujours oui |
Règle générale des cycles :
Les arbres sont bipartis par vacuité — sans cycle, il n'y a pas de cycle impair. On les colorie par la parité de la profondeur.
L'algorithme est un simple BFS (exercice A2) qui colorie au passage :
- colorier le sommet de départ en rouge ;
- à chaque découverte, colorier le nouveau sommet de la couleur opposée à son père ;
- si l'on rencontre une arête entre deux sommets déjà coloriés de la même couleur : le graphe n'est pas biparti, et cette arête ferme un cycle impair.
Sur
Sur
Coût :
La bipartition n'est pas qu'un exercice de coloriage : c'est la structure de tous les problèmes d'appariement.
| situation | groupe |
groupe |
|---|---|---|
| affectation de tâches | agents | tâches |
| emplois du temps | cours | créneaux |
| mariages stables | candidats | postes |
| recommandation | utilisateurs | produits |
Et c'est décisif algorithmiquement : le couplage maximal, qui reste polynomial en général mais exige l'algorithme « des fleurs » d'Edmonds, devient simple sur un graphe biparti (exercice C3), par réduction au flot maximal.
⚠️ La différence avec la coloration générale est frappante : tester si
Réponse :
Tri topologique d'un DAG
Pour le graphe orienté d'arcs
Indices (3)
Algorithme de Kahn : retirer un sommet de degré entrant nul, recommencer.
Degrés entrants :
Un cycle empêche tout sommet d'avoir un degré entrant nul à un moment.
Correction détaillée
Un tri topologique ordonne les sommets d'un graphe orienté de sorte que chaque arc aille de gauche à droite : si
C'est l'ordre dans lequel exécuter des tâches qui dépendent les unes des autres.
DAG = Directed Acyclic Graph, graphe orienté acyclique.
Arcs :
Le degré entrant d'un sommet est son nombre de prédécesseurs — c'est-à-dire le nombre de conditions à remplir avant de pouvoir le traiter.
| sommet | prédécesseurs | degré entrant |
|---|---|---|
| aucun | ||
| aucun | ||
Les sommets de degré entrant
Le principe : on retire un sommet sans prédécesseur, on met à jour les degrés entrants, et on recommence.
| étape | disponibles | on sort | on décrémente | sortie |
|---|---|---|---|---|
| 1 | ||||
| 2 | ||||
| 3 | ||||
| 4 | — | |||
| 5 | — |
Vérification — chaque arc doit aller de gauche à droite :
| arc | position source | position cible | OK ? |
|---|---|---|---|
| ✓ | |||
| ✓ | |||
| ✓ | |||
| ✓ |
⚠️ Le tri n'est pas unique :
On ajoute
| sommet | degré entrant |
|---|---|
Déroulons Kahn :
| étape | disponibles | on sort | résultat |
|---|---|---|---|
| 1 | |||
| 2 | — | BLOCAGE |
Aucun sommet de degré entrant nul, et il reste
La raison : le graphe contient un circuit,
Vérification : les arcs
Un circuit rend l'ordre impossible :
C'est la propriété la plus utile de l'algorithme, et il faut la savoir :
Si Kahn sort moins de
sommets, le graphe contient un circuit — et les sommets restants sont exactement ceux qui y participent ou en dépendent.
L'algorithme ne fait donc pas que trier : il certifie l'acyclicité. C'est le test de circuit le plus employé en pratique, et il coûte
⚠️ Le blocage n'est pas un échec de l'algorithme, c'est sa réponse. Un programme qui « plante » sur un cycle est mal écrit ; le bon comportement est de rendre « pas de tri, voici le circuit ».
Le tri topologique est partout où il y a des dépendances.
| domaine | sommets | arcs | ce qu'un circuit signifie |
|---|---|---|---|
| compilation | modules | « A importe B » | dépendance circulaire — refusée |
| gestion de projet | tâches | « A avant B » | planning impossible (exercice E4) |
| tableur | cellules | « A2 utilise B7 » | référence circulaire |
| build system | fichiers | dépendances | cible impossible à construire |
| cours | modules | prérequis | cursus impossible |
Le message « référence circulaire » d'un tableur, c'est exactement ce blocage de Kahn.
⚠️ Et l'ordre importe pour la suite du chapitre : la méthode PERT (exercice E4) commence par un tri topologique, parce que calculer une date au plus tôt suppose que toutes les tâches amont soient déjà datées.
Réponse :
Algorithme de Dijkstra
Graphe orienté pondéré :
Indices (3)
Dijkstra fixe à chaque étape le sommet non traité de plus petite distance, puis relâche ses arcs.
Initialiser
Relâcher
Correction détaillée
L'algorithme de Dijkstra calcule les plus courts chemins depuis une source, dans un graphe à poids positifs ou nuls.
Son principe est celui d'une tache d'huile : on connaît d'abord la distance au sommet le plus proche, puis au suivant, et ainsi de suite par distance croissante. À chaque tour, on fige définitivement le sommet non traité de plus petite distance provisoire.
⚠️ Cette garantie repose entièrement sur la positivité des poids — c'est ce que montrera l'exercice B6.
Départ : sommet
| tour | on extrait | on relâche | mises à jour | |
|---|---|---|---|---|
| 1 | ||||
| 2 | ||||
| 3 | ||||
| 4 | ||||
| 5 | — | fini |
Ordre d'extraction :
Le mot relâcher désigne le test
Tour 2 — le sommet
Tour 3 — le sommet
Sans ces deux relâchements, on aurait conclu
On remonte les pères depuis
Vérification par les poids :
Comparaison avec l'alternative
L'invariant : quand on extrait un sommet
Preuve en une phrase : tout autre chemin vers
C'est ici, et uniquement ici, que la positivité sert — et c'est ce qui tombe en défaut à l'exercice B6.
| complexité | |
|---|---|
| avec tas binaire | |
| avec tableau simple |
Pour un graphe peu dense (
Réponse :
dijkstra : Bellman-Ford & poids négatifs
Graphe orienté
Indices (3)
Dijkstra est exclu : il y a un poids négatif.
Bellman-Ford relâche toutes les arêtes
Comparer le chemin
Correction détaillée
Dijkstra exige des poids positifs ou nuls. Dès qu'un arc est négatif, il peut se tromper (exercice B6). Bellman–Ford n'a pas cette limite : il accepte les poids négatifs, au prix d'une méthode plus lente mais plus robuste.
Son principe est brutal et sûr : au lieu de choisir soigneusement quel sommet traiter, on relâche TOUS les arcs,
L'arc
Initialisation :
| après | ||||
|---|---|---|---|---|
| init | ||||
| passe 1 | ||||
| passe 2 | ||||
| passe 3 |
Détail de la passe 1, en traitant les arcs dans l'ordre donné :
: ; : ; : ✓ ; : .
Ici tout converge dès la première passe, parce que l'ordre des arcs suit le sens du graphe. Avec un ordre défavorable, il aurait fallu les trois passes — c'est pourquoi l'algorithme les fait toutes.
C'est le cœur de l'exercice.
Vers le sommet
| chemin | coût |
|---|---|
Le détour est MOINS cher que la route directe — c'est exactement ce que permet un poids négatif, et c'est ce qui ruine l'intuition géométrique. On ne peut plus se dire « s'éloigner coûte », puisqu'un arc peut rapporter.
Et l'effet se propage :
Un plus court chemin sans circuit a au plus
arcs (il ne repasse par aucun sommet, donc en visite au plus ).
Or chaque passe garantit au moins un arc de plus dans les chemins correctement calculés : après la passe
Après
⚠️ Et une
Ici, la passe supplémentaire n'améliore rien : pas de circuit négatif ✓
| Dijkstra | Bellman–Ford | |
|---|---|---|
| poids négatifs | non | oui |
| circuits négatifs | — | les détecte |
| complexité | ||
| stratégie | choisir le meilleur sommet | relâcher tout, plusieurs fois |
| parallélisable | difficilement | facilement (les relâchements d'une même passe sont indépendants) |
Bellman–Ford est nettement plus lent — quadratique au lieu de quasi-linéaire — mais il ne demande aucune hypothèse et il détecte les circuits négatifs. On l'emploie exactement quand Dijkstra ne s'applique pas.
Application réelle : le protocole de routage RIP en réseau est un Bellman–Ford distribué, chaque routeur relâchant les arcs qu'il connaît.
Réponse :
bellman_ford : Floyd-Warshall (toutes paires)
Sur le graphe de l'exercice B1, on veut les plus courtes distances entre toutes les paires. Quel algorithme et quelle est la distance de
Indices (3)
Floyd-Warshall calcule toutes les paires en
Pour
Les arcs utiles :
Correction détaillée
Dijkstra et Bellman–Ford calculent les distances depuis une source. Si l'on veut toutes les paires
L'algorithme repose sur une idée de programmation dynamique remarquablement simple :
= plus courte distance de à en n'utilisant comme sommets intermédiaires que .
La récurrence s'écrit alors : pour aller de
D'où le code, qui tient en trois lignes :
⚠️ L'ordre des boucles n'est pas négociable :
Matrice finale des distances (ligne = départ, colonne = arrivée) :
| de \ vers | |||||
|---|---|---|---|---|---|
Vérification à la main : de
| chemin | coût |
|---|---|
Le minimum est bien
Trois observations, toutes instructives :
(a) La première ligne est le résultat de Dijkstra de l'exercice B1 :
(b) Beaucoup de
(c) La diagonale est nulle :
| situation | algorithme | coût |
|---|---|---|
| une source, poids |
Dijkstra | |
| une source, poids quelconques | Bellman–Ford | |
| toutes les paires, graphe dense | Floyd–Warshall | |
| toutes les paires, graphe creux, poids |
Le point de bascule. Sur un graphe dense (
Ici :
Sa vraie force : Floyd–Warshall se transpose à d'autres « min-plus », comme la fermeture transitive (existe-t-il un chemin ?) en remplaçant
Réponse : Floyd–Warshall, en
floyd_warshall : Reconstruction du chemin
On a exécuté Dijkstra depuis
Indices (3)
Le tableau des prédécesseurs donne, pour chaque sommet, par où on l'a atteint au mieux.
Partir de la destination et remonter de prédécesseur en prédécesseur.
Inverser la liste obtenue.
Correction détaillée
Dijkstra rend les distances. Mais en pratique on veut le chemin : quel itinéraire suivre ? Le stocker en entier pour chaque sommet coûterait
La solution tient en un seul tableau : pour chaque sommet
Sur le graphe de l'exercice B1, Dijkstra a produit :
| sommet |
père |
posé au tour | |
|---|---|---|---|
| aucun (source) | — | ||
| tour 2 | |||
| tour 1 | |||
| tour 3 | |||
| tour 4 |
Le père se met à jour EN MÊME TEMPS que la distance. Quand le relâchement
⚠️ Noter que père
On part de la cible et on remonte les pères jusqu'à la source, puis on retourne la liste.
| étape | sommet courant | son père | chemin accumulé |
|---|---|---|---|
| 1 | |||
| 2 | |||
| 3 | |||
| 4 | |||
| 5 | aucun |
On retourne :
Vérification par les poids :
L'algorithme en pseudo-code :
(a) La somme des poids doit valoir la distance. C'est le contrôle décisif :
(b) Chaque arc du chemin doit exister.
(c) On doit atteindre la source. Si la remontée boucle ou s'arrête ailleurs, il y a une erreur.
⚠️ Le cas où il n'y a pas de chemin : si
L'ensemble des arcs
Quatre arcs pour cinq sommets — c'est bien
Propriété remarquable : ce seul arbre contient les plus courts chemins vers tous les sommets à la fois. Pour aller en
C'est ce qui rend les GPS possibles : on ne stocke pas un chemin par destination, mais un père par carrefour.
⚠️ Ne pas confondre avec l'arbre couvrant MINIMAL des exercices E1–E2 : celui-là minimise le poids total, celui-ci minimise chaque distance à la source. Ce sont deux arbres différents, obtenus par des algorithmes différents.
Réponse : on remonte les pères depuis la cible —
return_predecessors) : Détection d'un cycle négatif
Soit le graphe orienté
Indices (3)
Repérer le cycle entre
Si on peut tourner indéfiniment en diminuant le coût, la notion de plus court chemin s'effondre.
Bellman-Ford détecte une amélioration au
Correction détaillée
Question : que vaut le plus court chemin de
Réflexe naturel :
Repérons le circuit :
Ce circuit fait GAGNER
| chemin | coût |
|---|---|
| … |
⚠️ Ce n'est pas « la réponse est
L'algorithme fait
| après | |||
|---|---|---|---|
| init | |||
| passe 1 | |||
| passe 2 | |||
| passe 3 (test) |
(arcs traités dans l'ordre
La passe de contrôle AMÉLIORE encore des distances. C'est exactement le signal :
Pourquoi c'est un critère sûr. Un plus court chemin sans circuit a au plus
Bellman–Ford ne rend pas un résultat faux : il rend un diagnostic. C'est un comportement bien plus utile qu'une valeur arbitraire.
Le circuit négatif ne rend pas tout impossible — il faut savoir ce qui survit.
| question | réponse |
|---|---|
| plus court chemin de |
n'existe pas ( |
| plus court chemin élémentaire (sans répéter de sommet) | existe : |
| existe-t-il un chemin de |
oui — la connexité est intacte |
⚠️ Mais chercher le plus court chemin ÉLÉMENTAIRE en présence de poids négatifs est NP-difficile. Le problème « facile » devient donc « très dur » dès qu'on impose la non-répétition. C'est l'une des bascules les plus nettes du chapitre, à rapprocher de l'exercice D6.
C'est pourquoi les algorithmes de plus court chemin exigent l'absence de circuit négatif : sans cette hypothèse, le problème change de nature.
Un circuit négatif n'est pas une curiosité d'exercice.
| domaine | ce que serait un circuit négatif |
|---|---|
| arbitrage de devises | une suite de conversions qui crée de l'argent |
| comptabilité | une chaîne d'écritures qui se solde par un gain |
| ordonnancement | des contraintes de dates contradictoires |
| flots à coût minimal | un cycle qu'il faut annuler pour optimiser |
L'arbitrage est l'exemple le plus parlant. En posant
Cas particulier : un circuit de poids exactement nul ne pose aucun problème. Ce n'est pas la présence d'un circuit qui gêne, c'est son signe.
Réponse : le circuit
NegativeCycleError levée ✓)Pourquoi Dijkstra échoue en négatif
Expliquer pourquoi l'algorithme de Dijkstra peut donner un résultat faux sur un graphe à arcs de poids négatif.
Indices (3)
Dijkstra « fixe » définitivement un sommet dès qu'il a la plus petite distance provisoire.
Cette décision suppose qu'aucun chemin futur ne pourra faire mieux.
Un arc négatif viole cette hypothèse.
Correction détaillée
Pourquoi Dijkstra peut-il rendre un résultat faux sur un graphe à arcs négatifs ?
La réponse tient à l'invariant qui fonde l'algorithme (exercice B1) :
quand on extrait le sommet de distance provisoire minimale, cette distance est définitive.
Cet invariant repose entièrement sur une hypothèse : s'éloigner coûte. Un arc négatif la détruit.
Preuve de l'invariant, quand les poids sont
Il ne peut pas faire mieux.
L'étape marquée
Reprenons le graphe de l'exercice B2 :
Déroulons Dijkstra :
| tour | on extrait | on relâche | |
|---|---|---|---|
| 1 | |||
| 2 | |||
| 3 | |||
| 4 | — |
Le résultat de Dijkstra :
La vérité, par Bellman–Ford (exercice B2) :
Au tour 2, Dijkstra extrait le sommet
Au tour 3, il découvre que
- le sommet
est déjà extrait, donc l'algorithme ne le retraite pas ; - ses successeurs — ici
— ne sont donc jamais recalculés ; - l'erreur de
sur se propage en une erreur de sur .
⚠️ Le sommet
Leçon de méthode : vérifier un algorithme sur la seule valeur qu'on regarde ne suffit pas. Ici
Deux « réparations » viennent naturellement à l'esprit. Les deux échouent, et savoir pourquoi vaut mieux que de les essayer.
(a) « Ajouter une constante à tous les poids pour les rendre positifs. » Cela change le problème : un chemin de
Sur notre exemple avec
(b) « Autoriser la réextraction d'un sommet déjà figé. » L'algorithme devient correct (en l'absence de circuit négatif)… mais sa complexité explose — il peut extraire un sommet un nombre exponentiel de fois. On a alors réinventé, en moins bien, ce que Bellman–Ford fait proprement.
La seule bonne réponse est de changer d'algorithme.
| poids | algorithme |
|---|---|
| tous |
Dijkstra, |
| quelconques, sans circuit négatif | Bellman–Ford, |
| graphe acyclique, poids quelconques | tri topologique + relâchement, |
La troisième ligne est utile : sur un DAG (exercice A6), les poids négatifs ne posent aucun problème, et l'on est même plus rapide que Dijkstra.
Réponse : Dijkstra fige la distance d'un sommet à son extraction, ce qui n'est justifié que si les poids sont
Arbre couvrant minimal (Kruskal)
Graphe non orienté pondéré, arêtes (poids) :
Indices (3)
Trier les arêtes par poids croissant.
Ajouter chaque arête si elle ne crée pas de cycle (union-find).
S'arrêter à
Correction détaillée
Un arbre couvrant minimal (ACM) est un sous-graphe qui relie tous les sommets avec le poids total le plus petit possible, et sans cycle.
C'est le problème du câblage : relier
L'algorithme de Kruskal procède par arêtes, dans l'ordre croissant des poids :
on trie les arêtes par poids croissant, et on prend chacune sauf si elle crée un cycle.
C'est un algorithme glouton — il ne revient jamais sur ses choix — et, fait remarquable, il donne l'optimum exact.
Arêtes et poids :
Triées par poids croissant :
| arête | poids | crée un cycle ? | décision | composantes après |
|---|---|---|---|---|
| non | PRISE | |||
| non | PRISE | |||
| non | PRISE | |||
| non | PRISE | |||
| oui ( |
rejetée | — | ||
| oui | rejetée | — | ||
| oui | rejetée | — |
Contrôles :
arêtes pour sommets, soit ✓ (exercice E3) ; - une seule composante à la fin ✓ ;
- on s'arrête dès qu'on a
arêtes : les trois dernières n'ont même pas besoin d'être examinées.
C'est l'opération critique de l'algorithme, et sa mise en œuvre décide de la complexité.
Une arête
crée un cycle si et seulement si et sont déjà dans la même composante.
Vérifions sur
La structure adaptée est l'union–find (union par rang, compression de chemin), qui répond en temps quasi constant :
Le tri domine — c'est lui qui fixe la complexité.
C'est le point qui mérite explication : un algorithme glouton donne rarement l'optimum, ici toujours.
Propriété de la coupe. Soit une partition des sommets en deux groupes. L'arête de poids minimal qui les traverse appartient à un ACM.
Démonstration par échange : si un ACM ne la contenait pas, lui ajouter
Kruskal ne fait rien d'autre qu'appliquer cette propriété à répétition : chaque arête prise est la plus légère qui relie deux composantes encore séparées.
⚠️ Cette propriété est propre aux arbres couvrants. Le même raisonnement glouton sur le voyageur de commerce (exercice D2) ou le sac à dos (D1) échoue — c'est ce qui sépare les problèmes faciles des problèmes NP-difficiles.
Réponse : ACM
minimum_spanning_tree somme Prim vs Kruskal
Sur le graphe de E1, exécuter Prim depuis le sommet
Indices (3)
Prim fait croître un arbre depuis un sommet de départ.
À chaque étape, ajouter l'arête la moins chère sortant de l'arbre courant.
Continuer jusqu'à couvrir tous les sommets.
Correction détaillée
Kruskal (exercice E1) raisonne par arêtes, en partant du graphe entier. Prim raisonne par sommets, en partant d'un seul et en faisant grossir un arbre :
on part d'un sommet, et à chaque tour on ajoute l'arête la plus légère qui sort de l'arbre déjà construit.
C'est la même idée gloutonne, appliquée à une frontière qui avance — très proche de Dijkstra dans sa mécanique (un tas, une extraction du minimum), mais avec un critère différent.
Arêtes :
| tour | arbre courant | arêtes sortantes (poids) | la plus légère | on ajoute |
|---|---|---|---|---|
| 1 | sommet |
|||
| 2 | sommet |
|||
| 3 | sommet |
|||
| 4 | sommet |
|||
| 5 | — | — | fini |
⚠️ À chaque tour, on ne regarde QUE les arêtes qui sortent de l'arbre. Au tour 3, l'arête
| Kruskal | Prim | |
|---|---|---|
| poids total | ||
| arêtes | ||
| ordre d'ajout |
Même poids ET mêmes arêtes ✓
L'ordre coïncide ici, mais ce n'est pas une règle : Kruskal prend les arêtes par poids croissant dans tout le graphe, Prim par poids croissant au bord de son arbre. Sur un autre graphe, les ordres diffèrent — et les arbres aussi, si plusieurs ACM existent.
Quand les arbres peuvent-ils différer ? Uniquement si le graphe a plusieurs ACM, ce qui suppose des poids égaux. Ici les sept poids sont deux à deux distincts :
C'est un théorème : si tous les poids sont distincts, l'arbre couvrant minimal est unique. Les deux algorithmes devaient donc trouver le même — leur accord n'est pas une chance, c'est une conséquence.
| Kruskal | Prim (avec tas) | Prim (avec tableau) | |
|---|---|---|---|
| complexité | |||
| adapté aux graphes | creux | creux | denses |
| structure | union–find | tas de priorité | tableau |
| se parallélise | plutôt bien | mal | mal |
Le critère est la densité. Sur un graphe creux (
Prim ressemble beaucoup à Dijkstra (exercice B1) : même boucle, même tas, même extraction du minimum. La seule différence tient au critère — Dijkstra compare
Réponse : Prim depuis
prim=kruskalArbre couvrant : nombre d'arêtes
Pourquoi un arbre couvrant d'un graphe à
Indices (3)
Un arbre est connexe et sans cycle.
Un graphe connexe à
Ajouter une arête à un arbre crée un cycle.
Correction détaillée
Pourquoi un arbre couvrant d'un graphe à
sommets a-t-il exactement arêtes ?
La réponse est un théorème de structure, et il tient à la définition même d'un arbre : un graphe connexe et sans cycle.
Deux des trois propriétés — connexité, acyclicité,
Construisons l'arbre en ajoutant les sommets un par un.
Départ :
À chaque étape, on rattache un nouveau sommet à l'arbre déjà construit. Il faut exactement une arête :
- moins d'une (c'est-à-dire zéro) : le sommet resterait isolé, le graphe ne serait plus connexe ;
- plus d'une : le nouveau sommet aurait deux voisins déjà reliés entre eux — cela fermerait un cycle.
Après
Pour
Ajoutons une arête à un arbre à
Sur l'exercice E1 : l'ACM est
- ancien chemin de
à : ; - avec la nouvelle arête : le cycle
✓
C'est exactement la raison pour laquelle Kruskal l'a rejetée. Le test « crée un cycle ? » et le compte «
Et symétriquement : retirer une arête d'un arbre le déconnecte toujours en deux morceaux, puisqu'aucun chemin de secours n'existe. Un arbre est donc minimalement connexe — on ne peut rien enlever — et maximalement acyclique — on ne peut rien ajouter.
Pour un graphe à
| conséquence | |
|---|---|
| jamais connexe — au moins |
|
| arbre si et seulement si connexe (ou, ce qui revient au même, sans cycle) | |
| au moins un cycle, quelle que soit la disposition |
⚠️ Le cas
Le compte d'arêtes est nécessaire, jamais suffisant. Il faut toujours lui adjoindre la connexité ou l'acyclicité.
Le nombre
Ce compte est un contrôle immédiat de tout résultat du chapitre.
| situation | contrôle |
|---|---|
| ACM (E1, E2) | doit avoir exactement |
| arbre des plus courts chemins (B4) | pareil |
| parcours BFS/DFS (A2) | son arbre a |
| structure de données (arbre binaire) |
Un résultat qui n'a pas
Conséquence pratique en câblage : relier
Réponse :
Ordonnancement PERT — durée du projet
Un projet a
Indices (3)
Les dépendances forment un DAG ; on calcule les dates de début au plus tôt.
Date au plus tôt d'une tâche
La durée du projet est le plus long chemin.
Correction détaillée
Un projet est un ensemble de tâches avec des dépendances. La question est : combien de temps au minimum ?
La réponse n'est ni la somme des durées (les tâches indépendantes se font en parallèle) ni la plus longue tâche : c'est la longueur du chemin critique, le plus long chemin de dépendances.
⚠️ C'est un plus LONG chemin, pas un plus court — l'inverse des exercices du lot B. Le problème serait NP-difficile en général, mais il est facile sur un DAG (exercice A6) : il suffit de parcourir les tâches dans l'ordre topologique.
| tâche | durée | dépend de |
|---|---|---|
| — | ||
| — | ||
Le graphe :
Tri topologique (exercice A6) :
La date de début au plus tôt d'une tâche est la plus tardive des fins de ses prédécesseurs — il faut que tous soient finis :
| tâche | calcul | début | fin |
|---|---|---|---|
| aucun prédécesseur | |||
| aucun prédécesseur | |||
| fin |
|||
| fin |
⚠️ Le
Énumérons les quatre chemins complets et leurs durées :
| chemin | durées | total |
|---|---|---|
Le maximum,
Une tâche est critique quand tout retard sur elle retarde tout le projet.
Vérifions sur
Vérifions sur
C'est exactement ce que l'exercice E5 va chiffrer.
Le contraste est frappant : les deux tâches sont des prédécesseurs de
Chercher un plus long chemin est NP-difficile dans un graphe général — c'est essentiellement le problème du chemin hamiltonien.
Mais sur un DAG, c'est linéaire : sans circuit, on peut trier les sommets topologiquement, et chaque date se calcule une fois pour toutes à partir de celles déjà connues. Coût :
⚠️ D'où l'importance du tri topologique de l'exercice A6 : il n'est pas décoratif, il est ce qui rend PERT calculable. Et si le graphe de dépendances contenait un circuit — «
La méthode PERT (Program Evaluation and Review Technique) a été mise au point en 1958 pour le programme de missiles Polaris ; la méthode jumelle CPM (Critical Path Method) l'a été à la même époque (1957) chez DuPont. Toutes deux sont, mathématiquement, ce calcul de plus long chemin sur un DAG.
Réponse : la durée minimale est
Marges et tâches critiques
Pour le projet de E4 (durée
Indices (3)
Calculer les dates de début au plus tard (rétro-planning depuis la fin
Marge
Une tâche est critique si sa marge est nulle.
Correction détaillée
La marge d'une tâche est le retard qu'elle peut prendre sans retarder le projet. Elle se calcule en confrontant deux dates :
| date | définition | sens du calcul |
|---|---|---|
| au plus tôt |
le plus tôt où |
en avant, depuis le début |
| au plus tard |
le plus tard où |
en arrière, depuis la fin |
Une tâche de marge nulle est critique : elle n'a aucune souplesse.
| tâche | durée | tôt | fin au plus tôt |
|---|---|---|---|
Durée du projet :
On part de la fin et on remonte. La formule est :
et pour une tâche sans successeur,
⚠️ Attention à la formule : on prend le min des dates de début des successeurs, puis on retranche sa propre durée. L'erreur classique est de retrancher la durée du successeur — ce qui donne un résultat faux.
| tâche | successeurs | calcul | tard |
|---|---|---|---|
| aucun | |||
| aucun | |||
Le
| tâche | tôt | tard | marge | statut |
|---|---|---|---|---|
| CRITIQUE | ||||
| souple | ||||
| CRITIQUE | ||||
| souple | ||||
| CRITIQUE |
Contrôle décisif : ces trois tâches sont exactement le chemin critique
Ces trois vérifications sont la meilleure façon de s'assurer qu'on n'a pas confondu les dates. Une marge qui ne « résiste » pas au test est mal calculée.
Le chemin critique est ce qu'un chef de projet surveille en priorité : c'est la seule chaîne où un jour perdu est un jour perdu pour tout le monde.
| marge | ce que ça permet |
|---|---|
| rien — surveiller de près | |
| absorber un aléa, décaler le démarrage, lisser les ressources |
Deux usages concrets de la marge :
- lissage des ressources. Si
et demandent la même équipe, on peut décaler de sans coût. Cela libère la date , sans supprimer le chevauchement (il glisse sur ) : pour séparer et , il faudrait décaler de , au-delà de sa marge. - absorption des aléas. Une tâche de marge
tolère deux jours de retard imprévu ; une tâche critique, aucun.
⚠️ Le chemin critique BOUGE dès qu'on modifie le projet. Si l'on accélère
Il existe aussi une marge libre — le retard possible sans décaler la tâche suivante — toujours
Réponse : marges
pert_tard donne marges Piloter le délai d'un projet
Dans le projet de E4-E5, le chef de projet veut raccourcir la durée totale. Sur quelles tâches doit-il agir, et qu'apporte une accélération de
Indices (3)
Seules les tâches du chemin critique fixent la durée totale.
Accélérer une tâche à marge non nulle ne fait que consommer cette marge.
Le chemin critique est
Correction détaillée
Le chef de projet veut raccourcir la durée totale. Sur quelles tâches agir ?
Accélérer une tâche non critique ne sert à RIEN — elle ne fait qu'augmenter sa marge, déjà positive. C'est le résultat le plus contre-intuitif de la gestion de projet, et le plus utile.
On accélère chaque tâche d'une unité et l'on recalcule la durée du projet (initialement
| on accélère | marge | nouvelle durée | gain |
|---|---|---|---|
Le tableau est sans appel : les trois tâches critiques rapportent chacune une unité, les deux autres rien du tout.
Le chemin
Même en rendant
Cas particulier instructif :
Accélérer une tâche critique fonctionne — mais pas indéfiniment.
Accélérons
| chemin | avant | après |
|---|---|---|
Il y a maintenant DEUX chemins critiques, tous deux à
Conséquence : accélérer
C'est l'erreur classique du pilotage de projet : on optimise une fois, on croit pouvoir continuer sur la même tâche, et le gain s'arrête sans qu'on comprenne pourquoi.
La marche à suivre, à chaque itération :
- calculer le chemin critique ;
- parmi ses tâches, accélérer la moins chère ;
- recalculer — le chemin critique a pu changer ;
- recommencer tant que le gain vaut son prix.
Le meilleur candidat ici est
| tâche critique | sur combien de chemins | robustesse du gain |
|---|---|---|
| moyenne | ||
| la meilleure | ||
| moyenne |
C'est le problème du crashing en gestion de projet : chaque accélération a un coût, et l'on cherche le meilleur rapport gain de durée / coût. Formalisé, c'est un programme linéaire — ce qui referme la boucle avec le chapitre Optimisation.
Réponse : agir sur