DS MP - Réseau de distribution d'eau - Analyse de graphes orientés pondérés

20 Mar 2026 20 Mar 2026 276 vues ESSADDOUKI Mostafa 11 min de lecture
Complexité algorithmique
1 Introduction à l'analyse des algorithmes 2 Notations asymptotiques 3 Opérations élémentaires et modèles de coût 4 Complexité temporelle et spatiale 5 Méthode de comptage des pas 6 Méthode de comptage des pas pour les boucles
Diviser pour régner & algorithmes de tri
7 Rappel sur l'approche récursive 8 Diviser pour régner 9 la recherche dichotomique 10 Tri par fusion 11 Tri rapide 12 Analyse des fonctions récursives
Les arbres binaires
13 Introduction aux arbres binaires 14 Définitions récursives des arbres 15 Propriétés des arbres binaires 16 Types d'arbre binaire 17 Parcours en profondeur des arbres binaires 18 Parcours en largeur des arbres binaires 19 Exercices corrigés sur les arbres - TD 1 20 Exercices corrigés sur les arbres - TD 2 21 Exercices corrigés sur les arbres - TD 3 22 DS - Arbres binaires de recherche équilibrés (AVL) 23 DS - Codage de Huffman - Compression de données
Algorithmes gloutons
24 Introduction aux algorithmes gloutons 25 Problème de la sélection d'activités 26 Problème de séquencement des tâches 27 Problème du Sac à Dos fraction
Programmation dynamique
28 Introduction à la programmation dynamique 29 Le concept de sous-structure optimale 30 Le concept de sous-problèmes superposés 31 Méthodes de la programmation dynamique 32 Différence entre la programmation dynamique, l'approche diviser pour régner, et les algorithmes gloutons 33 Calculer les nombres de catalan en C++ et Python 34 Calculer le coefficient binomial 35 Le nombre de façons pour construire un mur de dimension 4*N 36 Défi de conversion de mots 37 Décomposition de phrases à partir d'un dictionnaire 38 La collection de pièces dans un labyrinthe 39 Nombre de façons de regrouper les étudiants 40 Compter tous les chemins possibles dans une grille MxN
Méta heuristique
41 Algorithmes heuristiques et métaheuristiques 42 Algorithme de Recuit simulé 43 Algorithme Colonies de fourmis
Théorie des graphes
44 Introduction et notions fondamentales 45 Chemins, cycles et connexité 46 Représentations des graphes 47 Parcours de graphes 48 Algorithme de Dijkstra 49 Algorithme A* 50 Algorithme de Bellman-Ford 51 Algorithme Floyd-Warshall 52 Coloration des graphes
Base de données et SQL
53 Introduction au langage SQL 54 Concepts de base de SGBDR 55 Syntaxes de différentes instructions SQL 56 Création et suppression d'une base de données 57 Opérateurs SQL 58 Les contraintes en SQL 59 Création et suppression des tables en SQL 60 Insertion et modifications des enregistrements 61 Extraction des données - SELECT 62 Filtrer les données - WHERE 63 Recherche de motifs - LIKE 64 Trier les données - ORDER BY 65 Les jointures en SQL - JOIN 66 Fonctions d'agrégation en SQL - SUM, COUNT, AVG, MIN et MAX 67 Organiser des données identiques en groupes - GROUP BY et HAVING 68 Les sous-requêtes en SQL 69 Les fonctions SQL de manipulation de date 70 DS - langage SQL 71 Exercices corrigés de langage SQL
Introduction à l'intelligence artificielle
Introduction à la théorie des jeux
Préparation aux concours
72 Réseau de distribution d'eau 73 Arbre d'expression arithmétique 74 Exploration d'une grotte souterraine
Hydraulique Graphes orientés Algorithmes de cheminement Avancé
6 parties
17 questions
Python - CPGE MP
~120 min

Réseau de distribution d'eau - Analyse de graphes orientés pondérés

Une ville souhaite modéliser son réseau de distribution d'eau. Le réseau est représenté par un graphe orienté pondéré où :

  • les sommets représentent des nœuds du réseau (châteaux d'eau, jonctions, quartiers),
  • les arêtes orientées représentent des canalisations avec une capacité maximale (en litres/seconde).

Représentation : On utilise un dictionnaire de listes d'adjacence :

# reseau[u] = liste de couples (v, cap) signifiant :
#   canalisation de u vers v, capacité cap

reseau1 = {
    0: [(1, 10), (2, 8)],
    1: [(3, 5),  (4, 7)],
    2: [(1, 3),  (4, 6)],
    3: [(5, 4)],
    4: [(5, 9)],
    5: []
}
# Sommet 0 = château d'eau (source), sommet 5 = quartier (puits)

reseau d'eau

0
Exploration du graphe 4 questions
Q1

Écrire une fonction voisins(reseau, u) qui retourne la liste des sommets directement accessibles depuis u.

Exemple

Entrée
voisins(reseau1, 2)
Sortie
[1, 4]
Solution — Voisins
def voisins(reseau, u):
    if u not in reseau:
        return []
    return [v for v, cap in reseau[u]]
Q2

Écrire une fonction nb_sommets(reseau) et une fonction nb_aretes(reseau) qui retournent respectivement le nombre de sommets et le nombre d'arêtes du graphe.

Exemple

Entrée
nb_sommets(reseau1)
nb_aretes(reseau1)
Sortie
6
7
Solution — Nombre de sommets et d'arêtes
def nb_sommets(reseau):
    return len(reseau)

def nb_aretes(reseau):
    total = 0
    for voisins in reseau.values():
        total += len(voisins)
    return total
Q3

Écrire une fonction parcours_largeur(reseau, s) qui retourne la liste des sommets visités en BFS depuis le sommet source s, dans l'ordre de découverte.

Exemple

Entrée
parcours_largeur(reseau1, 0)
Sortie
[0, 1, 2, 3, 4, 5]
Solution — Parcours en largeur (BFS)
def parcours_largeur(reseau, s):
    visites = []
    file = [s]
    vu = {s}
    
    while file:
        u = file.pop(0)
        visites.append(u)
        for v in voisins(reseau, u):
            if v not in vu:
                vu.add(v)
                file.append(v)
    
    return visites
Q4

Écrire une fonction est_accessible(reseau, s, t) qui retourne True si le sommet t est accessible depuis s en suivant les arêtes orientées, False sinon. On utilisera un parcours en largeur ou en profondeur.

Exemple

Entrée
est_accessible(reseau1, 0, 5)
est_accessible(reseau1, 3, 2)
Sortie
True
False
Solution — Accessibilité
def est_accessible(reseau, s, t):
    if s == t:
        return True
    visites = set()
    file = [s]
    visites.add(s)
    
    while file:
        u = file.pop(0)
        for v in voisins(reseau, u):
            if v == t:
                return True
            if v not in visites:
                visites.add(v)
                file.append(v)
    return False
1
Chemins et distances 3 questions
Q5

Écrire une fonction distance(reseau, s, t) qui retourne la longueur minimale (en nombre d'arêtes) d'un chemin de s à t, ou -1 si t n'est pas accessible depuis s. On utilisera un BFS.

Exemple

Entrée
distance(reseau1, 0, 5)
distance(reseau1, 0, 3)
distance(reseau1, 3, 0)
Sortie
2
2
-1
Solution — Distance minimale
def distance(reseau, s, t):
    if s == t:
        return 0
    
    dist = {s: 0}
    file = [s]
    
    while file:
        u = file.pop(0)
        for v in voisins(reseau, u):
            if v not in dist:
                dist[v] = dist[u] + 1
                if v == t:
                    return dist[v]
                file.append(v)
    
    return -1
Q6

Écrire une fonction chemin_court(reseau, s, t) qui retourne un chemin de longueur minimale de s à t sous forme de liste de sommets, ou [] si aucun chemin n'existe. On stockera pour chaque sommet son prédécesseur dans le BFS.

Exemple

Entrée
chemin_court(reseau1, 0, 5)
Sortie
[0, 2, 4, 5]  # ou [0, 1, 4, 5]
Solution — Chemin le plus court
def chemin_court(reseau, s, t):
    if s == t:
        return [s]
    
    precedent = {s: None}
    file = [s]
    
    while file:
        u = file.pop(0)
        for v in voisins(reseau, u):
            if v not in precedent:
                precedent[v] = u
                if v == t:
                    # Reconstruire le chemin
                    chemin = []
                    courant = t
                    while courant is not None:
                        chemin.append(courant)
                        courant = precedent[courant]
                    return chemin[::-1]
                file.append(v)
    
    return []
Q7

Écrire une fonction tous_chemins(reseau, s, t) qui retourne la liste de tous les chemins simples (sans sommet répété) de s à t, en utilisant un parcours en profondeur avec backtracking.

Exemple

Entrée
tous_chemins(reseau1, 0, 5)
Sortie
[[0,1,3,5], [0,1,4,5], [0,2,1,3,5], [0,2,1,4,5], [0,2,4,5]]
Solution — Tous les chemins (DFS + backtracking)
def tous_chemins(reseau, s, t):
    resultats = []
    
    def dfs(u, chemin):
        if u == t:
            resultats.append(chemin[:])
            return
        
        for v in voisins(reseau, u):
            if v not in chemin:
                chemin.append(v)
                dfs(v, chemin)
                chemin.pop()
    
    dfs(s, [s])
    return resultats
2
Capacités et goulots d'étranglement 3 questions
Q8

La capacité d'un chemin est le minimum des capacités de ses arêtes (c'est le débit maximal qu'on peut y faire passer).
Écrire une fonction capacite_chemin(reseau, chemin) qui retourne la capacité du chemin donné (liste de sommets). On supposera le chemin valide et non vide.

Exemple

Entrée
capacite_chemin(reseau1, [0, 1, 4, 5])
capacite_chemin(reseau1, [0, 2, 4, 5])
Sortie
7
6
Solution — Capacité d'un chemin
def capacite_chemin(reseau, chemin):
    min_cap = float('inf')
    for i in range(len(chemin) - 1):
        u, v = chemin[i], chemin[i+1]
        for w, cap in reseau[u]:
            if w == v:
                min_cap = min(min_cap, cap)
                break
    return min_cap
Q9

Écrire une fonction meilleur_chemin(reseau, s, t) qui retourne, parmi tous les chemins simples de s à t, celui qui a la capacité maximale. En cas d'égalité, on retourne le premier trouvé.

Exemple

Entrée
meilleur_chemin(reseau1, 0, 5)
Sortie
[0, 1, 4, 5]
Solution — Meilleur chemin (capacité maximale)
def meilleur_chemin(reseau, s, t):
    tous = tous_chemins(reseau, s, t)
    if not tous:
        return None
    
    meilleur = tous[0]
    meilleur_cap = capacite_chemin(reseau, meilleur)
    
    for chemin in tous[1:]:
        cap = capacite_chemin(reseau, chemin)
        if cap > meilleur_cap:
            meilleur_cap = cap
            meilleur = chemin
    
    return meilleur
Q10

On appelle goulot d'étranglement d'un chemin l'arête de capacité minimale. Écrire une fonction goulot(reseau, chemin) qui retourne le couple $(u, v)$ correspondant à cette arête (le premier rencontré en cas d'égalité).

Exemple

Entrée
goulot(reseau1, [0, 1, 4, 5])
Sortie
(1, 4)
Solution — Goulot d'étranglement
def goulot(reseau, chemin):
    min_cap = float('inf')
    goulot_arete = None
    
    for i in range(len(chemin) - 1):
        u, v = chemin[i], chemin[i+1]
        for w, cap in reseau[u]:
            if w == v:
                if cap < min_cap:
                    min_cap = cap
                    goulot_arete = (u, v)
                break
    
    return goulot_arete
3
Détection de cycles et tri topologique 2 questions
Q11

Écrire une fonction contient_cycle(reseau) qui retourne True si le graphe orienté contient au moins un cycle, False sinon. On utilisera un DFS avec trois états pour chaque sommet : non visité, en cours de visite, complètement visité.

Exemple

Entrée
reseau2 = {0: [(1,1)], 1: [(2,1)], 2: [(0,1)], 3: [(2,1)]}
contient_cycle(reseau1)
contient_cycle(reseau2)
Sortie
False
True
Solution — Détection de cycle (DFS 3 états)
def contient_cycle(reseau):
    # 0 = non visité, 1 = en cours, 2 = terminé
    etat = {sommet: 0 for sommet in reseau}
    
    def dfs(u):
        etat[u] = 1  # en cours de visite
        
        for v in voisins(reseau, u):
            if etat[v] == 1:  # cycle détecté
                return True
            if etat[v] == 0 and dfs(v):
                return True
        
        etat[u] = 2  # terminé
        return False
    
    for sommet in reseau:
        if etat[sommet] == 0:
            if dfs(sommet):
                return True
    return False
Q12

Lorsque le graphe est un DAG (graphe orienté acyclique), il est possible de le trier topologiquement : trouver un ordre linéaire des sommets tel que pour toute arête $(u,v)$, $u$ apparaît avant $v$.
Écrire une fonction tri_topologique(reseau) qui retourne une liste des sommets dans un ordre topologique valide, ou None si le graphe contient un cycle. On utilisera un DFS post-ordre.

Exemple

Entrée
tri_topologique(reseau1)
Sortie
[0, 2, 1, 4, 3, 5]  # (un ordre valide parmi plusieurs)
Solution — Tri topologique (DFS post-ordre)
def tri_topologique(reseau):
    # 0 = non visité, 1 = en cours, 2 = terminé
    etat = {sommet: 0 for sommet in reseau}
    resultat = []
    
    def dfs(u):
        etat[u] = 1
        for v in voisins(reseau, u):
            if etat[v] == 1:  # cycle détecté
                return False
            if etat[v] == 0 and not dfs(v):
                return False
        etat[u] = 2
        resultat.append(u)
        return True
    
    for sommet in reseau:
        if etat[sommet] == 0:
            if not dfs(sommet):
                return None
    
    return resultat[::-1]  # inversion pour l'ordre topologique
4
Plus court chemin pondéré 3 questions

On souhaite maintenant minimiser non plus le nombre d'arêtes, mais le coût total d'un chemin. On définit le coût d'une arête $(u,v,c)$ comme $100 - c$ (plus la capacité est grande, moins le chemin est « coûteux » à entretenir).

Q13

Écrire une fonction cout_arete(cap) qui retourne le coût d'une arête de capacité cap.

Exemple

Entrée
cout_arete(7)
Sortie
93
Solution — Coût d'une arête
def cout_arete(cap):
    return 100 - cap
Q14

Écrire une fonction dijkstra(reseau, s) qui implémente l'algorithme de Dijkstra et retourne un dictionnaire dist tel que dist[v] est le coût minimal d'un chemin de s à v (ou float('inf') si v n'est pas accessible). On utilisera une file de priorité (module heapq).

Exemple

Entrée
dijkstra(reseau1, 0)
Sortie
{0: 0, 1: 90, 2: 92, 3: 181, 4: 183, 5: 187}
Solution — Dijkstra
import heapq

def dijkstra(reseau, s):
    dist = {sommet: float('inf') for sommet in reseau}
    dist[s] = 0
    file = [(0, s)]
    
    while file:
        d, u = heapq.heappop(file)
        if d > dist[u]:
            continue
        
        for v, cap in reseau[u]:
            cout = cout_arete(cap)
            if dist[u] + cout < dist[v]:
                dist[v] = dist[u] + cout
                heapq.heappush(file, (dist[v], v))
    
    return dist
Q15

En modifiant dijkstra, écrire une fonction chemin_optimal(reseau, s, t) qui retourne le chemin de coût minimal de s à t sous forme de liste de sommets.

Exemple

Entrée
chemin_optimal(reseau1, 0, 5)
Sortie
[0, 1, 4, 5]
Solution — Chemin optimal (Dijkstra avec prédécesseurs)
def chemin_optimal(reseau, s, t):
    dist = {sommet: float('inf') for sommet in reseau}
    dist[s] = 0
    precedent = {sommet: None for sommet in reseau}
    file = [(0, s)]
    
    while file:
        d, u = heapq.heappop(file)
        if d > dist[u]:
            continue
        
        if u == t:
            break
        
        for v, cap in reseau[u]:
            cout = cout_arete(cap)
            if dist[u] + cout < dist[v]:
                dist[v] = dist[u] + cout
                precedent[v] = u
                heapq.heappush(file, (dist[v], v))
    
    if precedent[t] is None and s != t:
        return []
    
    chemin = []
    courant = t
    while courant is not None:
        chemin.append(courant)
        courant = precedent[courant]
    
    return chemin[::-1]
5
Application : fiabilité du réseau 2 questions

On dit qu'un sommet v (différent de la source s et du puits t) est un point de vulnérabilité si sa suppression rend t inaccessible depuis s.

Q16

Écrire une fonction points_vulnerabilite(reseau, s, t) qui retourne la liste de tous les points de vulnérabilité du réseau pour la paire $(s, t)$. On pourra s'appuyer sur est_accessible.

Exemple

Entrée
points_vulnerabilite(reseau1, 0, 5)
Sortie
[4]
Solution — Points de vulnérabilité
def points_vulnerabilite(reseau, s, t):
    if s == t:
        return []
    
    vulnérables = []
    
    for sommet in reseau:
        if sommet == s or sommet == t:
            continue
        
        # Créer une copie du réseau sans le sommet
        copie = {u: [(v, cap) for v, cap in voisinage if v != sommet]
                 for u, voisinage in reseau.items() if u != sommet}
        
        if not est_accessible(copie, s, t):
            vulnérables.append(sommet)
    
    return vulnérables
Q17

On appelle réseau robuste un réseau dans lequel il n'existe aucun point de vulnérabilité entre la source et le puits. Écrire une fonction est_robuste(reseau, s, t) qui retourne True si le réseau est robuste.

Exemple

Entrée
est_robuste(reseau1, 0, 5)
Sortie
False
Solution — Test de robustesse
def est_robuste(reseau, s, t):
    return len(points_vulnerabilite(reseau, s, t)) == 0
Sortie
// La sortie apparaîtra ici…
Prêt · Ctrl+Entrée pour exécuter

Discussion (0)

Soyez le premier à laisser un commentaire !

Laisser un commentaire

Votre commentaire sera visible après modération.