Exercice 1 : Programmation dynamique
Concours Blanc - Filière MPI
Section titled “Concours Blanc - Filière MPI”Épreuve d’Informatique - Algorithmique Avancée et Graphes
Section titled “Épreuve d’Informatique - Algorithmique Avancée et Graphes”Thème : Programmation dynamique et optimisation de séquences
Section titled “Thème : Programmation dynamique et optimisation de séquences”Introduction de l’examinateur
Section titled “Introduction de l’examinateur”Ce problème aborde un classique des concours de la filière MPI : l’optimisation par programmation dynamique. L’objectif est d’évaluer votre capacité à formaliser un problème d’optimisation sous forme de récurrence, à concevoir l’algorithme impératif ou récursif avec mémoïsation associé, et à analyser rigoureusement sa complexité en temps et en espace.
Les candidats doivent veiller à soigner la rigueur mathématique des indices et à justifier systématiquement les complexités annoncées.
Problème : Le Sablier d’Opaline
Section titled “Problème : Le Sablier d’Opaline”On considère une chaîne de caractères de longueur , constituée de caractères pris dans un alphabet fini .
Pour toute sous-chaîne (avec ), on cherche à déterminer le coût minimal pour la transformer en un “palindrome parfait” selon un jeu de règles de substitution donné. Toutefois, pour ce problème, nous allons nous intéresser à une variante plus algorithmique : le découpage optimal en palindromes.
On définit un palindrome comme une chaîne qui se lit de la même gauche à droite et de droite à gauche. On cherche à découper la chaîne en un nombre minimal de sous-chaînes contiguës qui soient toutes des palindromes. Exemple: * Pour , les découpages possibles en palindromes sont nombreux. Par exemple, (3 morceaux) ou (2 morceaux). Le nombre minimal de morceaux est ici 2.
Partie I : Analyse préliminaire et structure de substructure optimale
Section titled “Partie I : Analyse préliminaire et structure de substructure optimale”Question 1
Section titled “Question 1”Soit un prédicat booléen indiquant si la sous-chaîne (de l’indice inclus à l’indice inclus) est un palindrome. Donner une relation de récurrence permettant de calculer pour tous .
Corrigé : Un sous-caractère ou une chaîne vide est trivialement un palindrome. Pour une chaîne de longueur supérieure ou égale à 2, est un palindrome si et seulement si ses caractères extrêmes sont égaux () et que la chaîne centrale est également un palindrome.
Formellement :
Question 2
Section titled “Question 2”Soit le nombre minimal de palindromes composant le préfixe (pour ). Établir une relation de récurrence pour calculer en fonction des valeurs de pour et du prédicat .
Corrigé : Pour calculer , on peut examiner tous les points de coupe possibles (avec ). Si le suffixe est un palindrome (c’est-à-dire ), alors on peut former le préfixe en ajoutant ce palindrome à la fin de la solution optimale pour .
Par convention, si et que est vrai, le coût est de 1 (la chaîne entière est un palindrome).
La relation de récurrence s’écrit donc :
avec la convention .
Partie II : Conception de l’algorithme
Section titled “Partie II : Conception de l’algorithme”Question 3
Section titled “Question 3”Écrire une fonction en Python min_palindromes(S: str) -> int qui implémente cette approche par programmation dynamique.
Note: On veillera à précalculer le tableau des palindromes pour éviter les recalculs redondants.*
Corrigé :
def min_palindromes(S: str) -> int: n = len(S) if n == 0: return 0
# Étape 1 : Précalcul des palindromes P[i][j] # P[i][j] est un booleen indiquant si S[i..j] est un palindrome P = [[False] * n for _ in range(n)]
# Les sous-chaînes de longueur 1 sont des palindromes for i in range(n): P[i][i] = True
# Remplissage par longueur croissante for length in range(2, n + 1): for i in range(n - length + 1): j = i + length - 1 if length == 2: P[i][j] = (S[i] == S[j]) else: P[i][j] = (S[i] == S[j]) and P[i+1][j-1]
# Étape 2 : Calcul de M(i) le nombre minimal de coupes # M[i] correspond au résultat pour S[0..i] M = [float('inf')] * n
for i in range(n): if P[0][i]: M[i] = 0 # S[0..i] est déjà un palindrome complet else: for k in range(1, i + 1): if P[k][i]: M[i] = min(M[i], 1 + M[k - 1])
return M[n - 1]Partie III : Analyse de la complexité
Section titled “Partie III : Analyse de la complexité”Question 4
Section titled “Question 4”Déterminer la complexité en temps et en espace de la fonction min_palindromes en fonction de . Justifiez brièvement.
Corrigé :
-
Complexité en espace :
- La matrice de taille de booléens demande en mémoire.
- Le tableau de taille demande .
- L’espace total est donc de .
-
Complexité en temps :
- Le précalcul de la matrice comporte deux boucles imbriquées parcourant états, chaque transition s’effectuant en . Cette étape prend opérations.
- Le calcul du tableau utilise une boucle externe de taille et une boucle interne de taille au pire (allant jusqu’à ). Le nombre total d’itérations est , soit .
- Le temps total est donc de .
Analyse critique des résultats
Section titled “Analyse critique des résultats”Avis de l’examinateur : L’algorithme proposé est optimal en termes de temps pour ce problème spécifique (), surclassant l’approche naïve exponentielle .
Cependant, sur le plan de la complexité spatiale, l’allocation d’une matrice complète de taille peut s’avérer pénalisante en mémoire pour de très grandes chaînes (par exemple ). En pratique, on pourrait optimiser l’espace en remarquant que le calcul de la ligne de ne dépend que de la ligne , ce qui permettrait de réduire l’empreinte mémoire à en ne conservant que les deux dernières lignes, ou en utilisant un algorithme de type Manacher pour trouver les palindromes en temps et espace. Néanmoins, pour le cadre standard d’une épreuve de concours MPI, la solution en spatiale et temporelle valide l’intégralité des attendus du barème.