Implémentations Algorithmiques : Codage de Huffman, Graphes et Structures de Données

Codage de Huffman et Optimisation Greedy

Pour minimiser le coût total d'un arbre de codage, l'approche gloutonne de Huffman est optimale. En utilisant une file de priorité (tas binaire), nous extrayons répétitivement les deux poids les plus faibles pour construire l'arbre de bas en haut.

import heapq

def calculer_cout_huffman():
    n = int(input())
    poids = list(map(int, input().split()))
    file_priorite = []
    
    for p in poids:
        heapq.heappush(file_priorite, p)
        
    cout_total = 0
    while len(file_priorite) > 1:
        premier = heapq.heappop(file_priorite)
        second = heapq.heappop(file_priorite)
        somme = premier + second
        cout_total += somme
        heapq.heappush(file_priorite, somme)
        
    print(cout_total)

Plus Courts Chemins Multi-Sources (Floyd-Warshall)

L'algorithme de Floyd-Warshall permet de calculer les distances minimales entre toutes les paires de sommets. Pour reconstruire le chemin, nous maintenons une matrice de successeurs mise à jour lors de chaque relaxation.

from collections import defaultdict
from math import inf

def resoudre_plus_court_chemin():
    nb_points = int(input())
    nom_vers_id = {}
    id_vers_nom = []
    
    for i in range(nb_points):
        nom = input()
        nom_vers_id[nom] = i
        id_vers_nom.append(nom)
        
    adj = [[inf] * nb_points for _ in range(nb_points)]
    nb_aretes = int(input())
    suivant = [[-1] * nb_points for _ in range(nb_points)]
    
    for _ in range(nb_aretes):
        u_nom, v_nom, poids = input().split()
        u, v = nom_vers_id[u_nom], nom_vers_id[v_nom]
        p = int(poids)
        if p < adj[u][v]:
            adj[u][v] = adj[v][u] = p
            suivant[u][v] = v
            suivant[v][u] = u

    for k in range(nb_points):
        for i in range(nb_points):
            for j in range(nb_points):
                if adj[i][k] + adj[k][j] < adj[i][j]:
                    adj[i][j] = adj[i][k] + adj[k][j]
                    suivant[i][j] = suivant[i][k]

    nb_requetes = int(input())
    for _ in range(nb_requetes):
        dep, dest = input().split()
        u, v = nom_vers_id[dep], nom_vers_id[dest]
        curr = u
        while curr != v:
            prochain = suivant[curr][v]
            print(f"{id_vers_nom[curr]}->({adj[curr][prochain]})->", end="")
            curr = prochain
        print(id_vers_nom[v])

Distance Minimale entre Composantes (DFS + BFS)

Ce problème consiste à identifier une première île via un parcours en profondeur (DFS), puis à utiliser un parcours en largeur (BFS) multi-sources pour trouver la distance la plus courte vers une seconde île.

from collections import deque

def distance_isolee():
    taille = int(input())
    grille = [list(input()) for _ in range(taille)]
    visite = [[False] * taille for _ in range(taille)]
    file_bfs = deque()
    directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]

    def marquer_ile(r, c):
        pile = [(r, c)]
        visite[r][c] = True
        while pile:
            curr_r, curr_c = pile.pop()
            file_bfs.append((curr_r, curr_c, 0))
            for dr, dc in directions:
                nr, nc = curr_r + dr, curr_c + dc
                if 0 <= nr < taille and 0 <= nc < taille:
                    if not visite[nr][nc] and grille[nr][nc] == '1':
                        visite[nr][nc] = True
                        pile.append((nr, nc))

    trouve = False
    for i in range(taille):
        if trouve: break
        for j in range(taille):
            if grille[i][j] == '1':
                marquer_ile(i, j)
                trouve = True
                break

    while file_bfs:
        r, c, d = file_bfs.popleft()
        if grille[r][c] == '1' and d > 0:
            print(d - 1)
            return
        for dr, dc in directions:
            nr, nc = r + dr, c + dc
            if 0 <= nr < taille and 0 <= nc < taille and not visite[nr][nc]:
                visite[nr][nc] = True
                file_bfs.append((nr, nc, d + 1))

Programmation Dynamique sur Arbre Binaire

Pour un arbre binaire complet, nous utilisons la DP pour calculer le gain maximal. Pour chaque nœud, on stocke deux états : le gain maximum en incluant le nœud actuel ou en l'excluant.

def resoudre_dp_arbre():
    n = int(input())
    valeurs = [0] + list(map(int, input().split()))
    dp_inclure = [0] * (n + 1)
    dp_exclure = [0] * (n + 1)

    for i in range(n, 0, -1):
        gauche = i * 2
        droite = i * 2 + 1
        dp_inclure[i] = valeurs[i]
        
        if gauche <= n:
            dp_inclure[i] += dp_exclure[gauche]
            dp_exclure[i] += max(dp_inclure[gauche], dp_exclure[gauche])
        if droite <= n:
            dp_inclure[i] += dp_exclure[droite]
            dp_exclure[i] += max(dp_inclure[droite], dp_exclure[droite])
            
    print(max(dp_inclure[1], dp_exclure[1]))

Chemin Eulérien et Algorithme de Hierholzer

La recherche d'une séquence de mots enchaînés revient à trouver un chemin eulérien dans un graphe dirigé où les sommets sont des lettres. Nous utilisons les degrés entrants et sortants pour valider l'existence du chemin.

import sys
sys.setrecursionlimit(2000)

def trouver_catenymes():
    nb_mots = int(input())
    mots = sorted([input() for _ in range(nb_mots)])
    adj = defaultdict(list)
    d_in, d_out = defaultdict(int), defaultdict(int)
    lettres = set()

    for idx, w in enumerate(mots):
        u, v = w[0], w[-1]
        adj[u].append((v, w, idx))
        d_out[u] += 1
        d_in[v] += 1
        lettres.update([u, v])

    depart, arrivee, possible = None, None, True
    for c in lettres:
        diff = d_out[c] - d_in[c]
        if diff == 1:
            if depart is not None: possible = False
            depart = c
        elif diff == -1:
            if arrivee is not None: possible = False
            arrivee = c
        elif diff != 0:
            possible = False

    if not possible:
        print("***")
        return

    depart = depart if depart else min(lettres)
    resultat, utilise = [], [False] * nb_mots

    def hierholzer(u):
        for v, mot, i in adj[u]:
            if not utilise[i]:
                utilise[i] = True
                hierholzer(v)
                resultat.append(mot)

    hierholzer(depart)
    if len(resultat) != nb_mots:
        print("***")
    else:
        print(".".join(resultat[::-1]))

Arbre de Segment avec Propagation Paresseuse (Lazy Propagation)

L'arbre de segment permet des mises à jour et des requêtes sur des intervalles en O(log N). La teechnique "lazy" retarde les mises à jour pour optimiser les performances sur les modifications de plages.

class ArbreSegment:
    def __init__(self, n):
        self.n = n
        self.arbre = [0] * (4 * n)
        self.paresse = [0] * (4 * n)

    def _pousser(self, node):
        if self.paresse[node] != 0:
            val = self.paresse[node]
            self.arbre[2 * node] += val
            self.paresse[2 * node] += val
            self.arbre[2 * node + 1] += val
            self.paresse[2 * node + 1] += val
            self.paresse[node] = 0

    def mettre_a_jour(self, node, debut, fin, l, r, v):
        if l <= debut and fin <= r:
            self.arbre[node] += v
            self.paresse[node] += v
            return
        self._pousser(node)
        milieu = (debut + fin) // 2
        if l <= milieu:
            self.mettre_a_jour(2 * node, debut, milieu, l, r, v)
        if r > milieu:
            self.mettre_a_jour(2 * node + 1, milieu + 1, fin, l, r, v)
        self.arbre[node] = max(self.arbre[2 * node], self.arbre[2 * node + 1])

    def requete(self, node, debut, fin, l, r):
        if l <= debut and fin <= r:
            return self.arbre[node]
        self._pousser(node)
        milieu = (debut + fin) // 2
        res = -float('inf')
        if l <= milieu:
            res = max(res, self.requete(2 * node, debut, milieu, l, r))
        if r > milieu:
            res = max(res, self.requete(2 * node + 1, milieu + 1, fin, l, r))
        return res

Étiquettes: Python graph-theory Huffman-Coding dynamic-programming segment-tree

Publié le 10 août à 08h46