Chemins de valeur optimale
Cours · algorithmique (école d'ingénieur), chapitre 5 · école d'ingénieur, informatique · le début du chapitre, la suite sur Adloun
Objectifs du chapitre
- Reconnaître un problème de chemin optimal : une suite de déplacements élémentaires, chacun avec un coût ou une valeur.
- Écrire la récurrence en regardant le dernier déplacement, d'abord sur une ligne de villes, puis sur une grille.
- Traiter proprement les cas particuliers (bords de la grille, cases inaccessibles) et choisir un ordre de calcul.
Au chapitre 1, Zébulon cherchait un chemin de valeur maximum en sautant de case en case. Ce chapitre généralise cette situation : des villes reliées par des lignes de bus, puis un robot qui se déplace sur une grille. Dans tous les cas, la question clé est la même : d'où vient-on, au dernier pas ?
5.1 Deux lignes de bus
Les villes sont alignées et desservies par deux lignes de bus. Depuis la ville on peut aller :
- à la ville , pour un coût ;
- directement à la ville , pour un coût .
On veut un trajet de coût minimum de la ville à la ville .
et (les cases marquées n'ont pas de sens : il n'y a pas de ville ou ). Le trajet coûte .
Supposons le problème résolu
Notons le coût minimum d'un trajet de la ville à la ville . Un trajet optimal vers (avec ) arrive par l'un des deux derniers trajets possibles :
- depuis , par la ligne courte : le trajet de à est alors optimal (sinon on l'améliorerait), et le coût total est ;
- depuis , par la ligne longue : coût .
Base. ; .
Hérédité. :
On calcule par croissants, en :
static int[] calculerM(int[] D1, int[] D2) { int n = D1.length;
// retourne M[0:n] de terme général M[j] = m(j)
int[] M = new int[n];
M[0] = 0; // base
if (n > 1) M[1] = D1[0]; // base
for (int j = 2; j < n; j++) // hérédité, par j croissants
M[j] = Math.min(M[j-1] + D1[j-1], M[j-2] + D2[j-2]);
return M;
}
Pour afficher le trajet, on n'a pas mémorisé d'arguments : on retrouve le dernier trajet en testant laquelle des deux égalités est vérifiée. Notons un trajet de coût minimum de à : si , et sinon.
static void afficherCheminMinimum(int[] M, int[] D1, int[] D2, int j) {
// affiche tr(j). Appel principal : afficherCheminMinimum(M, D1, D2, n-1)
if (j == 0) { System.out.print("0"); return; } // tr(0) a été affiché
if (M[j] == M[j-1] + D1[j-1]) { // dernier trajet : j-1 -> j
afficherCheminMinimum(M, D1, D2, j-1); // tr(j-1) a été affiché
System.out.printf("--(%d)-->%d", D1[j-1], j);
} else { // dernier trajet : j-2 -> j
afficherCheminMinimum(M, D1, D2, j-2); // tr(j-2) a été affiché
System.out.printf("--(%d)-->%d", D2[j-2], j);
} // donc tr(j) a été affiché
}
$ java BusDeuxLignes
M = [0, 10, 30, 60, 90]
Coût minimum m(4) = 90 : 0--(10)-->1--(50)-->3--(30)-->4
Attention
L'appel avec est correct : , donc on passe par la première branche et l'on ne consulte jamais . Vérifiez toujours, pour une fonction d'affichage, que les petits cas ne provoquent pas d'accès hors du tableau.
Ici le minimum est atteint deux fois pour : et . Il y a deux trajets optimaux ; la fonction affiche celui qu'elle teste en premier.
La suite du cours est sur Adloun
Le chapitre en entier, formules et figures comprises, avec le tuteur, les fiches, les QCM et les exercices.
Lire la suite du coursEn accès libre, sans inscription.