Probleme – Rendre le chemin, pas seulement sa longueur
Exercice · niveau 3 (difficile) · informatique (MP2I/MPI), chapitre 19 — Parcours de graphes et plus courts chemins
Énoncé
Les trois algorithmes du chapitre calculent des distances. On veut les chemins.
- Modifier le parcours en largeur pour qu'il rende un tableau de pères, et écrire la reconstruction.
- Prouver que le tableau des pères définit bien une arborescence, et que le chemin reconstruit est de longueur minimale.
- Faire de même pour Dijkstra, et pour Floyd-Warshall.
Corrigé
1. Le parcours en largeur avec pères.
(* Renvoie (d, pere) : d.(v) est la distance en nombre d'arcs (-1 si inaccessible)
et pere.(v) le sommet depuis lequel v a ete decouvert (-1 pour la source
et pour les inaccessibles).
Precondition : 0 <= s < Array.length g. Complexite : Theta(n + m). *)
let largeur_peres g s =
let n = Array.length g in
let d = Array.make n (-1) and pere = Array.make n (-1) in
let f = Queue.create () in
d.(s) <- 0; Queue.push s f;
while not (Queue.is_empty f) do
let u = Queue.pop f in
List.iter (fun v ->
if d.(v) = -1 then begin
d.(v) <- d.(u) + 1;
pere.(v) <- u; (* on retient QUI a decouvert v *)
Queue.push v f
end) g.(u)
done;
(d, pere)
(* Chemin de s a v, ou [] si v est inaccessible.
Precondition : pere provient d'un parcours lance depuis s.
Complexite : Theta(longueur du chemin). *)
let chemin pere s v =
let rec remonter v acc =
if v = s then s :: acc
else if v = -1 then [] (* inaccessible : on rend la liste vide *)
else remonter pere.(v) (v :: acc)
in remonter v []
Mesure sur le graphe à huit sommets du premier exercice :
d = 0 1 1 2 2 2 3 4
pere = - 0 0 1 1 2 3 6
0 -> 3 : 0 1 3 0 -> 6 : 0 1 3 6
0 -> 5 : 0 2 5 0 -> 7 : 0 1 3 6 7
Chaque chemin a bien arcs.
2. Les deux preuves.
C'est une arborescence. Chaque sommet accessible autre que reçoit un père une seule fois — la ligne pere.(v) <- u est sous le test d.(v) = -1, qui n'est vrai qu'au premier passage. Le graphe des arcs a donc exactement arcs pour sommets accessibles. Il est de plus sans cycle : , donc la fonction décroît strictement en remontant, et une remontée ne peut pas boucler. Un graphe connexe à arcs et sans cycle est un arbre (chapitre chap:arbres).
Terminaison de chemin. Le variant est : il décroît strictement de à chaque remontée, et il est minoré par , atteint exactement en .
Le chemin est de longueur minimale. La suite a exactement arcs par la décroissance ci-dessus, et le théorème du chapitre dit que est la distance minimale. Le chemin est donc optimal — et non pas seulement « un chemin ».
3. Dijkstra et Floyd-Warshall.
Dijkstra : on écrit pere.(v) <- u dans le relâchement, à côté de la mise à jour de d.(v) — et pas ailleurs. Un sommet peut changer de père plusieurs fois, la dernière valeur étant la bonne, puisque le relâchement final est celui qui a produit la distance définitive. La reconstruction est la même fonction. Mesure sur le graphe pondéré déroulé plus haut : pere = [-;0;0;2;5;2], d'où pour le sommet .
Floyd-Warshall : il n'y a pas de source, donc pas d'arborescence — il faut une matrice. On maintient , le premier sommet après sur un plus court chemin de à :
/* Initialisation : suivant[u][u] = u, suivant[u][v] = v si l'arc u->v
existe, et -1 sinon. */
if (d[u][k] + d[k][v] < d[u][v]) {
d[u][v] = d[u][k] + d[k][v];
suivant[u][v] = suivant[u][k]; /* on part comme pour aller vers k */
}
/* Reconstruction : Theta(longueur du chemin). */
void afficher_chemin(int suivant[][N_MAX], int u, int v) {
if (suivant[u][v] == -1) { printf("aucun chemin\n"); return; }
printf("%d", u);
while (u != v) { u = suivant[u][v]; printf(" %d", u); }
printf("\n");
}
La ligne à comprendre est suivant[u][v] = suivant[u][k] : puisque le nouveau chemin de à commence par aller de à , son premier pas est celui du chemin de à . On stocke le premier sommet et non le dernier, ce qui donne une reconstruction dans le bon sens, sans renversement.
Le coût de la reconstruction, dans les trois cas : de mémoire par sommet (ou par couple), et à la lecture. C'est la même leçon que le chapitre chap:dynamique : on garde la décision, pas la valeur — un tableau d'entiers suffit à retrouver tous les chemins.
Les autres exercices de ce chapitre Le cours du chapitre
Un blocage sur cet exercice ? Le tuteur d'Adloun guide par questions, sans donner la réponse.