Université d’Aix-Marseille Algorithmique L2 Informatique - Mathématiques 2016/2017 TP 7 : algorithme de Dijkstra Ce TP est consacré à la programmation de l’algorithme de Dijkstra. On enregistre un graphe orienté pondéré sous forme d’un fichier ASCII dont — la première ligne contient le nombre de sommets du graphe — la seconde ligne contient le nombre d’arcs du graphe — chaque ligne suivante décrit un arc sour la forme : sommet1, sommet2, poids Dessinez le graphe correspondant au fichier suivant. 6 10 0 1 0 2 1 2 1 4 2 3 2 4 3 1 3 4 3 5 4 5 19 8 14 6 4 22 2 10 11 2 — Un graphe sera représenté par un type structuré comprenant deux champs : — nbSommets : le nombre de sommets du graphe — Adj : les listes d’adjacence du graphe. — Adj est un tableau d’éléments de type LISTE, où LISTE décrit des listes chaı̂nées de couples (sommet,poids) ; Adj[v] est la liste des sommets adjacents au sommet v ; seules les nbSommets premières cases de Adj sont pertinentes pour le graphe. — La fonction void litGraphe(char *adr, GRAPHE *G) saisit un graphe à partir d’un fichier. #define POIDS_MAX 50 // poids maximum #define NB_SOM_MAX 10 // nombre de sommets maximum /* liste cha^ ınée de couples (sommet, poids) */ typedef struct maillon{ struct maillon *suiv; int nom; int poids; } MAILLON, *LISTE; 1 /* structure de graphe */ typedef struct graphe{ int nbSommets; LISTE Adj[NB_SOM_MAX]; // liste d’adjacence } GRAPHE; /* pour charger un graphe à partir d’un fichier */ void litGraphe(char *adr, GRAPHE *G){ FILE *f; int sa,sb,pds,nbArcs; f=fopen(adr,"r"); fscanf(f,"%d",&(G->nbSommets)); initAdjGraphe(G); // à écrire fscanf(f,"%d",&nbArcs); while (nbArcs){ fscanf(f,"%d %d %d",&sa,&sb,&pds); insere(sa,sb,pds, G->Adj); // à écrire nbArcs--; } fclose(f); } On pourra utiliser la fonction main suivante : int main(void){ GRAPHE G; litGraphe("./graphe.txt", &G); afficheGraphe(G); dijkstra(0, G); return 0; } Écrivez les fonctions suivantes : 1. /* insère (som_b,poids) en t^ ete dans la liste d’adjacence Adj[som_a] */ void insere(int som_a,int som_b, int poids, LISTE Adj[]){ ... } 2. /* initialisation de la table d’adjacence : toutes les listes cha^ ınées sont vides */ void initAdjGraphe(GRAPHE *G){ ... } 3. /* affichage d’un graphe : le nombre de sommets, puis chaque arc pondéré : (sommet_1, sommet_2, poids) */ void afficheGraphe(GRAPHE G){ ... } 4. /* algorithme de Dijkstra : calcule (et affiche) les tableaux dist et pred */ void dijkstra(int s, GRAPHE G){...} Dans un premier temps, vous pourrez implémenter l’ensemble F des sommets à explorer comme un tableau de G.nbSommets éléments tels que F [i] = 1 si le sommet i est dans F et F [i] = 0 sinon. 2 5. Complétez l’algorithme précédent pour qu’il affiche les chemins les plus courts entre s et tous les sommets de G (du coup, on n’aura plus besoin qu’il affiche les tableaux dist et pred. Pour cela, vous pourrez aussi écrire des fonctions — int affiche plus court chemin(int s, int t, int pred[]) qui affiche le plus court chemin de s à t et renvoie 0 s’il n’existe pas, et — void affiche plus courts chemins(int s, int nbSommets, int pred[], int dist[]) qui affiche tous les plus courts chemins à partir de s, avec leur distance. Plus Plus Plus Plus Plus Plus court court court court court court chemin chemin chemin chemin chemin chemin de de de de de de 2 2 2 2 2 2 à à à à à à 0 1 2 3 4 5 : : : : : : sommet non atteint. 2 3 1 . Distance : 6 2 . Distance : 0 2 3 . Distance : 4 2 3 1 4 . Distance : 12 2 3 1 4 5 . Distance : 14 6. (difficile) Si l’on implémente F comme un tableau, la recherche du sommet i pour lequel dist[i] est minimale est linéaire : d’où une complexité de O(n2 ) au total, où n =G.nbSommets. Si l’on implémente F comme une file de priorité, c’est-à-dire un tasmin muni d’une fonctionnalité de mise à jour, on obtient une complexité de O(n log n). Écrivez cette variante de l’algorithme de Dijkstra : void dijkstraFP(int s, GRAPHE G). Pour cela, — vous introduirez les variables int f[NB SOM MAX], nf, ind f[NB SOM MAX], où f représente le tasmin prioritarisé par dist 1 , nf est le nombre d’éléments du tasmin et ind f[i] donne l’indice du sommet i dans le tasmin f ; — la fonction int extrait min(int f[], int ind f[], int *nf, int dist[]) extrait le min de f, c’est-à-dire f[0], tout en préservant la structure de tasmin et en actualisant ind f — la fonction void maintient(int f[], int ind f[], int i, int dist[]) qui maintient la structure de f et la sémantique de ind f lorsque dist[i] a été diminué pour un sommet i de f. 7. (difficile) La structure que nous avons considérée pour les listes d’adjacence est midynamique (les listes d’adjacences elles-mêmes), mi-statique (on déclare un tableau de NB SOM MAX listes d’adjacence). Comment faire en sorte que la structure soit totalement dynamique, c’est-à-dire ne dépende pas de la constante NB SOM MAX ? 1. si i est un ascendant de j, alors dist[i]dist[j]. 3