Solution de Voisins entrants dans un graphe orienté - Sujet 01 - EP NSI 2025
Énoncé du problème
EXERCICE 1 (10 points)
On considère dans cet exercice un graphe orienté représenté sous forme de listes d’adjacence.
On suppose que les sommets sont numérotés de 0 à n-1.
Par exemple, le graphe suivant:
flowchart TD
n3((3)) --> n0((0))
n0 --> n1((1))
n0 --> n2((2))
n2 --> n0
n1 --> n2
[Figure : graphe orienté à 4 sommets numérotés 0, 1, 2 et 3, représentés par des ellipses. Une flèche va de 3 vers 0, une flèche va de 0 vers 1, une flèche va de 0 vers 2, une flèche va de 2 vers 0, et une flèche va de 1 vers 2.]
est représenté par la liste d’adjacence suivante:
adj = [[1, 2], [2], [0], [0]]
Écrire une fonction voisins_entrants(adj, x) qui prend en paramètre le graphe donné sous forme de liste d’adjacence et qui renvoie une liste contenant les voisins entrants du sommet x, c’est-à-dire les sommets y tels qu’il existe une arête de y vers x.
Exemples:
>>> voisins_entrants([[1, 2], [2], [0], [0]], 0)
[2, 3]
>>> voisins_entrants([[1, 2], [2], [0], [0]], 1)
[0]
EXERCICE 2 (10 points)
On considère dans cet exercice la suite de nombre suivante : 1, 11, 21, 1211, 111221, …
Cette suite est construite ainsi : pour passer d’une valeur à la suivante, on la lit et on l’écrit sous la forme d’un nombre. Ainsi, pour 1211 :
- on lit un 1, un 2, deux 1 ;
- on écrit donc en nombre 1 1, 1 2, 2 1 ;
- puis on concatène 111221.
Compléter la fonction nombre_suivant qui prend en entrée un nombre sous forme de chaine de caractère et qui renvoie le nombre suivant par ce procédé, encore sous forme de chaîne de caractère.
def nombre_suivant(s):
'''Renvoie le nombre suivant de celui representé par s
en appliquant le procédé de lecture.'''
resultat = ''
chiffre = s[0]
compte = 1
for i in range(...):
if s[i] == chiffre:
compte = ...
else:
resultat += ... + ...
chiffre = ...
...
lecture_... = ... + ...
resultat += lecture_chiffre
return resultat
Exemples
>>> nombre_suivant('1211')
'111221'
>>> nombre_suivant('311')
'1321'
Les contraintes ci-dessous sont ajoutées par la plateforme et ne font pas partie du sujet.
Exercice 1
Contraintes :
adjest une liste denlistes d’entiers, avec1 <= n <= 10^3- les sommets sont numérotés de
0àn-1: pour tout sommety,adj[y]est la liste des sommets vers lesquels part une arête issue dey 0 <= adj[y][i] <= n-1pour toutyet touti- les valeurs d’une même liste
adj[y]sont deux à deux distinctes (pas d’arête en double) - le nombre total d’arêtes est au plus
10^4 xest un entier avec0 <= x <= n-1- la liste renvoyée contient les sommets
yrangés par ordre croissant, comme dans les exemples du sujet
Exercice 2
Contraintes :
sest une chaîne de caractères non vide composée uniquement de chiffres décimaux0à91 <= len(s) <= 10^3- toute suite de chiffres identiques consécutifs de
sest de longueur au plus9, de sorte que chaque groupe se lit avec un seul chiffre de comptage - la valeur renvoyée est une chaîne de caractères
Solution
Exercice 1 - Les voisins entrants
L’idée. La liste d’adjacence donne, pour chaque sommet y, la liste adj[y] des sommets vers lesquels y pointe : ce sont les voisins sortants. La question demande l’inverse, et cette information n’est écrite nulle part telle quelle. Il faut donc la reconstruire : on passe en revue tous les sommets y du graphe, et on garde ceux dont la liste adj[y] contient x.
Un petit exemple. Avec adj = [[1, 2], [2], [0], [0]] et x = 0 : adj[0] = [1, 2] ne contient pas 0, adj[1] = [2] non plus, adj[2] = [0] contient 0 donc on garde 2, et adj[3] = [0] aussi donc on garde 3. On obtient [2, 3].
def voisins_entrants(adj, x):
entrants = []
# On parcourt les sommets dans l'ordre 0, 1, 2, ... : la liste
# obtenue est donc déjà rangée par ordre croissant
for y in range(len(adj)):
if x in adj[y]:
entrants.append(y)
return entrants
entrants est la liste que l’on construit petit à petit ; on la renvoie à la fin. Le test x in adj[y] pose simplement la question « existe-t-il une arête de y vers x ? ». Comme la boucle visite les sommets dans l’ordre croissant, les valeurs sont ajoutées dans le bon ordre : il n’y a aucun tri à faire ensuite.
Exercice 2 - La suite « lecture »
L’idée. Lire un nombre, c’est le découper en groupes de chiffres identiques qui se suivent, puis écrire, pour chaque groupe, sa longueur suivie du chiffre. La difficulté est qu’on ne connaît la longueur d’un groupe qu’une fois ce groupe terminé. On avance donc caractère par caractère en retenant deux choses : chiffre, le chiffre du groupe en cours, et compte, le nombre de fois qu’on vient de le voir. Tant que le caractère lu est le même, on augmente compte ; dès qu’il change, le groupe est fini : on l’écrit dans resultat, puis on repart sur un nouveau groupe de longueur 1.
Un petit exemple. '311' se découpe en 3, puis 11 : on lit un 3, deux 1, on écrit donc 13 puis 21, ce qui donne '1321'.
def nombre_suivant(s):
'''Renvoie le nombre suivant de celui representé par s
en appliquant le procédé de lecture.'''
resultat = ''
chiffre = s[0]
compte = 1
for i in range(1, len(s)):
if s[i] == chiffre:
compte = compte + 1
else:
# Le groupe en cours s'arrête ici : on l'écrit avant de repartir à zéro
resultat += str(compte) + chiffre
chiffre = s[i]
compte = 1
# Aucun changement ne suit le dernier groupe : la boucle ne l'a pas écrit
lecture_chiffre = str(compte) + chiffre
resultat += lecture_chiffre
return resultat
Le premier caractère est déjà pris en compte avant la boucle (chiffre = s[0], compte = 1), c’est pourquoi la boucle démarre à 1 et non à 0 : sinon on compterait ce chiffre deux fois. str(compte) transforme le nombre en caractère, pour pouvoir le coller devant chiffre.
Le point sur lequel on se trompe le plus souvent est la ligne lecture_chiffre, après la boucle. Un groupe n’est écrit que lorsqu’on rencontre un chiffre différent ; or le dernier groupe de la chaîne n’est suivi de rien. Sans cette ligne finale, '1211' donnerait '1112' au lieu de '111221' : les deux derniers 1 seraient comptés mais jamais écrits.