Tourner en rond
On souhaite se diriger à l'intérieur d'une ville, et notamment savoir s'il est possible de tourner en rond.
On représente la circulation dans une ville par un graphe orienté, où chaque sommet correspond à un lieu de la ville et chaque arête au sens de circulation entre deux lieux.
Exemple de plan de circulation
flowchart LR
A([Lycée]) --> B([Mairie])
B --> A
B --> E([Médiathèque])
C([Port]) --> B
B --> C
D([Ecole]) --> G([Maison])
E --> B
E --> D
G --> E
E --> F([Stade])
F --> G
Pour aller au Stade depuis le Lycée, on pourra emprunter le chemin Lycée - Mairie - Médiathèque - Stade.
Il est à noter que depuis la Médiathèque, on peut revenir à la Médiathèque en passant par l'Ecole puis la Maison.
Imaginons maintenant qu'il y a des travaux sur certaines routes qui sont désormais fermées. La circulation devient alors :
flowchart LR
A([Lycée]) --> B([Mairie])
B --> E([Médiathèque])
C([Port]) --> B
D([Ecole]) --> G([Maison])
E --> D
E --> F([Stade])
F --> G
Dans ce nouveau graphe, il n'y a plus de chemin possible depuis la Maison vers le Port.
De plus, quel que soit le lieu, il n'existe pas de chemin qui part de ce lieu et qui y revient.
On représente ce graphe par un dictionnaire dans lequel :
-
les clés sont les chaînes de caractères correspondant aux noms des lieux,
-
les valeurs associées sont des listes de chaînes de caractères représentant les lieux vers lesquels on peut se diriger.
1. Fonction existe_chemin
On se demande si, connaissant un plan de circulation, il est possible de se rendre d'un point de départ à un point d'arrivée.
Vous devez donc d'écrire une fonction existe_chemin qui :
-
prend en paramètre un dictionnaire
graphereprésentant une telle circulation, une chaîne de caractèresdepartqui représente le lieu de départ et une chaîne de caractèresarriveequi représente le lieu d'arrivée ; -
renvoie
Truesi un chemin existe entre le lieu de départ et le lieu d'arrivée,Falsesinon.
Exemples
>>> circulation = {
... "Lycee": ["Mairie"],
... "Port": ["Mairie"],
... "Mairie": ["Lycee", "Mediatheque", "Port"],
... "Mediatheque": ["Mairie", "Ecole", "Stade"],
... "Ecole": ["Maison"],
... "Stade": ["Maison"],
... "Maison": ["Mediatheque"],
... }
>>> existe_chemin(circulation, "Lycee", "Mediatheque")
True
>>> circulation_en_travaux = {
... "Lycee": ["Mairie"],
... "Port": ["Mairie"],
... "Mairie": ["Mediatheque"],
... "Mediatheque": ["Ecole", "Stade"],
... "Ecole": ["Maison"],
... "Stade": ["Maison"],
... "Maison": [],
... }
>>> existe_chemin(circulation_en_travaux, "Maison", "Port")
False
Aide
On pourra utiliser un parcours du graphe.
À ce titre, on fournit les classes Pile et File dont on donne les interfaces ci-dessous.
Ces deux classes sont déjà importées dans les différents éditeurs.
Interface de la classe Pile
-
p = Pile(): crée une pile vide et l'affecte à la variablep; -
p.est_vide(): renvoie le booléen indiquant si la pilepest vide ; p.empile(x): empile l'élémentxdans la pilep;p.depile(): dépile un élément de la pilepsi elle n'est pas vide et le renvoie. Provoque une erreur si la pile est vide.
La classe Pile
class Pile:
def __init__(self):
"""Initialise une pile vide"""
self.valeurs = []
def est_vide(self):
"""Renvoie un booléen : la pile est-elle vide ?"""
return len(self.valeurs) == 0
def empile(self, element):
"""Empile un élément au sommet de la pile"""
self.valeurs.append(element)
def depile(self):
"""
Dépile un élément au sommet et le renvoie.
Provoque une erreur si la pile est vide.
"""
if self.est_vide():
raise ValueError("Erreur, pile vide")
else:
return self.valeurs.pop()
def __repr__(self):
"""Affiche la pile, en indiquant le sommet"""
return f"| {' | '.join([str(x) for x in self.valeurs])} | <- sommet"
Interface de la classe File
-
f = File(): crée une file vide et l'affecte à la variablef; -
f.est_vide(): renvoie le booléen indiquant si la filefest vide ; f.enfile(x): enfile l'élémentxdans la filef;f.defile(): défile un élément de la filefsi elle n'est pas vide et le renvoie. Provoque une erreur si la file est vide.
La classe File
from collections import deque
class File:
"""Classe définissant une structure de file"""
def __init__(self):
self.valeurs = deque([])
def est_vide(self):
"""Renvoie le booléen True si la file est vide, False sinon"""
return len(self.valeurs) == 0
def enfile(self, x):
"""Place x à la queue de la file"""
self.valeurs.appendleft(x)
def defile(self):
"""Retire et renvoie l'élément placé à la tête de la file.
Provoque une erreur si la file est vide
"""
if self.est_vide():
raise ValueError("La file est vide")
return self.valeurs.pop()
def __repr__(self):
"""Convertit la file en une chaîne de caractères"""
if self.est_vide():
return "∅"
return f"(queue) {' -> '.join(str(x) for x in self.valeurs)} (tête)"
# Tests (insensible à la casse)(Ctrl+I)
(Alt+: ; Ctrl pour inverser les colonnes)
(Esc)
# Tests (insensible à la casse)(Ctrl+I)
(Alt+: ; Ctrl pour inverser les colonnes)
(Esc)
2. Fonction contient_cycle
Un promeneur se demande s'il est possible, connaissant le plan de circulation, de faire au moins une promenade débutant et se terminant au même endroit de la ville : une promenade allant du Lycée au Lycée ou une autre allant du Port au Port, etc.
Si au moins une telle promenade existe, on dit que le graphe représentant le plan de circulation contient un cycle.
Vous devez donc d'écrire une fonction contient_cycle qui :
-
prend en paramètre un dictionnaire
graphereprésentant une telle circulation ; -
renvoie
Truesi le graphe contient un cycle,Falsesinon.
La fonction existe_chemin de la question précédente est déjà importée dans cet éditeur.
Exemple
>>> circulation = {
... "Lycee": ["Mairie"],
... "Port": ["Mairie"],
... "Mairie": ["Lycee", "Mediatheque", "Port"],
... "Mediatheque": ["Mairie", "Ecole", "Stade"],
... "Ecole": ["Maison"],
... "Stade": ["Maison"],
... "Maison": ["Mediatheque"],
... }
>>> contient_cycle(circulation)
True
>>> circulation_en_travaux = {
... "Lycee": ["Mairie"],
... "Port": ["Mairie"],
... "Mairie": ["Mediatheque"],
... "Mediatheque": ["Ecole", "Stade"],
... "Ecole": ["Maison"],
... "Stade": ["Maison"],
... "Maison": [],
... }
>>> contient_cyle(circulation_en_travaux)
False
# Tests (insensible à la casse)(Ctrl+I)
(Alt+: ; Ctrl pour inverser les colonnes)
(Esc)
# Tests(insensible à la casse)(Ctrl+I)
(Alt+: ; Ctrl pour inverser les colonnes)
(Esc)