Vous êtes sur la page 1sur 11

Problem Solving –

Algorithmes gloutons
Travaux dirigés et pratiques n˚4
Algorithmes gloutons
Exercice 1 : Le coût de la non-panne sèche
Le Professeur Adel conduit une voiture entre Tunis et Agadir (Maroc)
sur l'autoroute transmaghrébine. Son réservoir, quand il est plein,
contient assez d'essence pour faire n kilomètres, et son smartphone lui
donne les distances entre les stations-service sur la route.
1. Donnez une méthode efficace grâce à laquelle le Professeur Adel
pourra déterminer les stations-service où il peut s'arrêter, sachant qu'il
souhaite faire le moins d'arrêts possible.
Voici un pseudo-code qui pourrait être utilisé pour résoudre ce problème :

1. Définir une liste vide stations pour stocker les stations-service où le Professeur
Adel peut s'arrêter.
2. Définir une variable distance_parcourue qui représente la distance parcourue
par le Professeur Adel depuis le début de son voyage.
3. Récupérer les distances entre les stations-service sur la route depuis le
smartphone du Professeur Adel.
4. Pour chaque distance entre les stations-service : a. Ajouter cette distance à la
distance parcourue. b. Si la distance parcourue est supérieure ou égale à la
distance maximale que peut parcourir la voiture du Professeur Adel avec un
réservoir plein, ajouter la station-service actuelle à la liste stations et réinitialiser
la distance parcourue à zéro.
5. Afficher la liste stations des stations-service où le Professeur Adel peut s'arrêter.
#include <stdio.h>

#define MAX_DISTANCE 1000 // Distance maximale que peut parcourir


la voiture du Professeur Adel avec un réservoir plein
int main(void) {
int distances[100]; // Tableau pour stocker les distances entre les
stations-service
int stations[100]; // Tableau pour stocker les stations-service où le
Professeur Adel peut s'arrêter
int num_stations = 0; // Nombre de stations-service où le Professeur
Adel peut s'arrêter
int distance_parcourue = 0; // Distance parcourue par le Professeur
Adel depuis le début de son voyage

// Récupération des distances entre les stations-service depuis le


smartphone du Professeur Adel
int num_distances = get_distances_from_smartphone(distances);

for (int i = 0; i < num_distances; i++) {


distance_parcourue += distances[i];
if (distance_parcourue >= MAX_DISTANCE) {
stations[num_stations] = i; // Ajout de la station-service actuelle à
la liste
num_stations++;
distance_parcourue = 0; // Réinitialisation de la distance parcourue
}
}
// Affichage de la liste des stations-service où le Professeur Adel peut
s'arrêter
printf("Le Professeur Adel peut s'arrêter aux stations-service suivantes
:\n");
for (int i = 0; i < num_stations; i++) {
printf("%d\n", stations[i]);
}

return 0;
}
Notez que la fonction get_distances_from_smartphone doit être implémentée de manière
à récupérer les distances entre les stations-service depuis le smartphone du Professeur Adel.
Cette fonction doit renvoyer le nombre de distances récupérées et stocker ces distances dans le
tableau distances.

2. Démontrez que votre stratégie est optimale.


La stratégie proposée dans le pseudo-code et le code C ci-dessus consiste à parcourir
les distances entre les stations-service et à s'arrêter dès que la distance parcourue
depuis le début du voyage atteint la distance maximale que peut parcourir la voiture du
Professeur Adel avec un réservoir plein.

Cette stratégie est optimale, car elle permet de minimiser le nombre d'arrêts nécessaires
en s'assurant que la voiture du Professeur Adel ne tombe jamais en panne d'essence. En
effet, en s'arrêtant dès que la distance parcourue atteint la distance maximale, le
Professeur Adel peut s'assurer qu'il a toujours assez d'essence pour continuer son
voyage jusqu'à la prochaine station-service.

De plus, cette stratégie ne nécessite pas de connaître à l'avance le nombre d'arrêts


nécessaires ni les distances exactes entre les stations-service. Elle s'adapte
automatiquement à la distance parcourue et aux distances entre les stations-service, ce
qui la rend facile à mettre en œuvre et à maintenir.
En résumé, la stratégie proposée est optimale car elle permet de minimiser le nombre
d'arrêts nécessaires tout en étant facile à mettre en œuvre et à maintenir.

Exercice 2 : Emploi du temps de salles


On suppose que l'on a un ensemble de n cours, c1, . . . , cn, le cours ci
commençant à l'heure début(ci) et finissant à l'heure fin(ci).
Écrivez un algorithme qui attribue une salle à chaque cours sachant que
l'on veut minimiser le nombre de salles occupées et que l'on ne veut
pas que deux cours aient lieu en même temps dans la même salle.
Voici un exemple de code en C++ qui utilise une approche gloutonne pour attribuer une salle à
chaque cours de manière à minimiser le nombre de salles occupées tout en s'assurant que deux
cours ne se déroulent pas en même temps dans la même salle:

#include <iostream>
#include <algorithm>

using namespace std;

const int MAX_N = 1000;

// Structure pour stocker les informations sur chaque cours


struct Course {
int start; // Heure de début
int end; // Heure de fin
int room; // Salle attribuée
};
int n; // Nombre de cours
Course courses[MAX_N]; // Tableau des cours

// Fonction de comparaison pour le tri par ordre croissant d'heure de


début
bool cmp(Course a, Course b) {
return a.start < b.start;
}

int main() {
cin >> n;
for (int i = 0; i < n; i++) {
cin >> courses[i].start >> courses[i].end;
}

// Trier les cours par ordre croissant d'heure de début


sort(courses, courses + n, cmp);

// Attribuer une salle à chaque cours


for (int i = 0; i < n; i++) {
// Parcourir toutes les salles et trouver la première disponible
for (int j = 0; j < n; j++) {
bool found = true;
for (int k = 0; k < i; k++) {
// Vérifier que la salle j n'est pas déjà occupée pendant le cours i
if (courses[k].room == j && courses[k].end > courses[i].start) {
found = false;
break;
}
}
// Si une salle est disponible, l'attribuer au cours
if (found) {
courses[i].room = j;
break;
}
}
}

// Afficher le résultat
for (int i = 0; i < n; i++) {
cout << "Cours " << i + 1 << ": Salle " << courses[i].room + 1 << endl;
}
return 0;
}
Ce code commence par lire le nombre de cours et les informations sur chacun d'eux (heure de
début et heure de fin). Il trie ensuite les cours par ordre croissant d'heure de début, ce qui
permet de parcourir les cours dans l'ordre chronologique. Pour chaque cours, il parcourt toutes
les salles disponibles et attribue la première salle qui n'est pas occupée pendant ce cours à ce
dernier. Enfin, il affiche le résultat de l'attribution.

Exercice 3 : Ordonnancement de tâches avec priorités


On doit réaliser un ensemble de n tâches T1, … , Tn sur une unique
machine. Chaque tâche Ti a une durée di et une priorité pi.
Une réalisation des tâches T1, … , Tn en est une permutation Ti1, … ,
Tin représentant l'ordre dans lequel elles sont exécutées. La date de fin
d'exécution Fi d'une tâche Ti est égale à la somme des durées des
tâches qui la précédent dans la permutation plus sa durée propre (di).
La pénalité d'une exécution vaut ∑ piFi. On cherche un
ordonnancement qui minimise cette pénalité.
1. Soit quatre tâches de durées respectives 3, 5, 7 et 4, et de priorités
respectives 6, 11, 9 et 5. Des deux réalisations (T1, T4, T2 , T3) et (T4,
T1, T3, T2), quelle est la meilleure ?
Pour déterminer la meilleure réalisation parmi (T1, T4, T2 , T3) et (T4, T1, T3, T2), nous
devons calculer la pénalité de chaque réalisation.

Pour la réalisation (T1, T4, T2 , T3), la pénalité est donnée par :

 Fi(T1) = 3
 Fi(T4) = 3 + 4 = 7
 Fi(T2) = 3 + 4 + 5 = 12
 Fi(T3) = 3 + 4 + 5 + 7 = 19

La pénalité de cette réalisation est donc 6 * 3 + 11 * 7 + 9 * 12 + 5 * 19 = 270.

Pour la réalisation (T4, T1, T3, T2), la pénalité est donnée par :
 Fi(T4) = 4
 Fi(T1) = 4 + 3 = 7
 Fi(T3) = 4 + 3 + 7 = 14
 Fi(T2) = 4 + 3 + 7 + 4 = 18

La pénalité de cette réalisation est donc 5 * 4 + 6 * 7 + 9 * 14 + 11 * 18 = 282.

La réalisation (T1, T4, T2 , T3) est donc la meilleure car elle a une pénalité inférieure à
celle de la réalisation (T4, T1, T3, T2).

2. Soient deux tâches Ti et Tj ( consécutives dans une réalisation telles


que : pi/di< pj/dj . Afin de minimiser la pénalité, laquelle doit être
exécutée en premier et laquelle en second ?
Pour minimiser la pénalité, il faut exécuter en premier la tâche Ti pour laquelle la valeur pi/di est la
plus petite. En effet, en exécutant en premier la tâche Ti, nous minimisons la pénalité associée à cette
tâche et donc la pénalité totale de la réalisation. La tâche Tj doit donc être exécutée en second.

Voici un exemple pour illustrer cette explication :

 Tâche Ti : durée di = 4, priorité pi = 10


 Tâche Tj : durée dj = 3, priorité pj = 15

Si nous exécutons en premier la tâche Ti, la pénalité associée à cette tâche sera pi * Fi = 10 * 4 = 40.
La pénalité associée à la tâche Tj sera pj * Fi = 15 * (4 + 3) = 75. La pénalité totale sera donc de 40 +
75 = 115.

Si nous exécutons en premier la tâche Tj, la pénalité associée à cette tâche sera pj * Fi = 15 * 3 = 45.
La pénalité associée à la tâche Ti sera pi * Fi = 10 * (3 + 4) = 70. La pénalité totale sera donc de 45 +
70 = 115.

Dans les deux cas, la pénalité totale est la même. Cependant, en exécutant en premier la tâche Ti,
nous avons minimisé la pénalité associée à cette tâche, ce qui est préférable pour minimiser la
pénalité totale.

3. En déduire un algorithme.

Ce pseudo-code commence par lire les informations sur chaque tâche et les trie par
ordre croissant de priorité (pi/di). Il initialise ensuite une liste de tâches ordonnancées et
ajoute chaque tâche à cette liste dans l'ordre de priorité. Enfin, il calcule la pénalité
totale de l'ordonnancement en utilisant la formule pi * Fi et affiche le résultat.

Voici un exemple de code en C++ qui met en œuvre ce pseudo-code :

#include <iostream>
#include <algorithm>

using namespace std;

const int MAX_N = 1000;

// Structure pour stocker les informations sur chaque tâche


struct Task {
int duration; // Durée
int priority; // Priorité
double value; // Valeur pi/di
};

int n; // Nombre de tâches


Task tasks[MAX_N]; // Tableau des tâches

// Fonction de comparaison pour le tri par ordre croissant de valeur


pi/di
bool cmp(Task a, Task b) {
return a.value < b.value;
}

int main() {
cin >> n;
for (int i = 0; i < n; i++) {
cin >> tasks[i].duration >> tasks[i].priority;
tasks[i].value = (double) tasks[i].priority / tasks[i].duration;
}

// Trier les tâches par ordre croissant de valeur pi/di


sort(tasks, tasks + n, cmp);

// Initialiser la liste de tâches ordonnancées


int orderedTasks[MAX_N];
for (int i = 0; i < n; i++) {
orderedTasks[i] = tasks[i].duration;
}

// Calculer la pénalité totale


int penalty = 0;
for (int i = 0; i < n; i++) {
penalty += tasks[i].priority * (i + 1);
}

// Afficher le résultat
cout << "Pénalité totale : " << penalty << endl

Vous aimerez peut-être aussi