Probleme – Le chemin le plus large
Exercice · niveau 3 (difficile) · informatique (MP2I/MPI), chapitre 23 — Unir & trouver, arbres couvrants
Énoncé
Le chapitre mentionne l'adaptation au chemin le plus large. On la construit ici.
Le goulot d'un chemin est le poids de son arête la plus faible. Étant donné un graphe non orienté connexe pondéré et deux sommets et , on cherche le chemin de à dont le goulot est maximal.
- Montrer que dans un arbre couvrant maximal , l'unique chemin de à est un chemin de goulot maximal.
- En déduire un algorithme et sa complexité.
- Vérifier sur un exemple.
Corrigé
1. La preuve. Soit un arbre couvrant maximal — obtenu par Kruskal en triant par poids décroissant —, soit l'unique chemin de à dans , et soit son goulot. Montrons qu'aucun chemin ne fait mieux.
Soit une arête de réalisant le goulot, . Retirer de coupe l'arbre en deux parties et , avec et (car est sur le chemin de à ). Toute arête traversant cette coupe vérifie : sinon, serait un arbre couvrant de poids strictement supérieur, ce qui contredit la maximalité de . C'est la propriété de la coupe, retournée.
Or tout chemin de à dans le graphe doit traverser cette coupe au moins une fois, donc emprunter une arête de poids . Son goulot est donc . Le chemin est optimal.
2. L'algorithme.
(* Chemin de goulot maximal entre s et t.
Entrees : n sommets, liste d'aretes (u, v, poids), graphe connexe.
Sortie : le goulot maximal. Complexite : O(m log m). *)
let goulot_maximal n aretes s t =
(* 1. arbre couvrant MAXIMAL : Kruskal, tri DECROISSANT *)
let triees = List.sort (fun (_,_,a) (_,_,b) -> compare b a) aretes in
let u = creer n and arbre = Array.make n [] in
List.iter (fun (a, b, p) ->
if unir u a b then begin
arbre.(a) <- (b, p) :: arbre.(a);
arbre.(b) <- (a, p) :: arbre.(b)
end) triees;
(* 2. l'unique chemin de s a t dans l'arbre, par parcours en profondeur *)
let vu = Array.make n false in
let rec descendre x mini =
if x = t then Some mini
else begin
vu.(x) <- true;
List.fold_left (fun acc (y, p) ->
match acc with
| Some _ -> acc
| None -> if vu.(y) then None else descendre y (min mini p)
) None arbre.(x)
end
in
descendre s max_int
Complexité : pour le tri, puis un parcours en dans un arbre de arêtes. Et le même arbre répond à toutes les paires : le construire une fois coûte , puis chaque requête coûte . C'est l'argument décisif quand on a beaucoup de paires à traiter.
3. La vérification. Sur le graphe à cinq sommets
l'arbre couvrant maximal calculé est , de poids . On compare, pour les dix paires de sommets, le goulot dans cet arbre au goulot optimal calculé par énumération de tous les chemins simples :
| paire | 0-1 | 0-2 | 0-3 | 0-4 | 1-2 | 1-3 | 1-4 | 2-3 | 2-4 | 3-4 |
|---|---|---|---|---|---|---|---|---|---|---|
| force brute | 3 | 3 | 3 | 3 | 5 | 5 | 5 | 7 | 6 | 6 |
| dans l'arbre | 3 | 3 | 3 | 3 | 5 | 5 | 5 | 7 | 6 | 6 |
Accord sur les dix paires.
Remarquer -. L'arête directe pèse ; le chemin optimal l'évite et passe par , de goulot . Le chemin le plus large n'est ni le plus court ni le moins cher : c'est un troisième problème, et il se trouve que l'arbre couvrant le résout aussi.
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.