| Thème de l'épreuve | Logimage |
| Principaux outils utilisés | algorithmique, programmation, complexité, programmation dynamique |
| Mots clefs | logimage, grille |
ECOLE POLYTECHNIQUE - ESPCI
ECOLES NORMALES SUPERIEURES
CONCOURS D'ADMISSION 2024
JEUDI 18 AVRIL 2024
16h30 - 18h30
FILIERES MP-MPI-PC-PSI
Epreuve n° 8
INFORMATIQUE B (XELSR)
Durée : 2 heures
L'utilisation des calculatrices n'est pas
autorisée pour cette épreuve
Logimage
Le sujet comporte 7 pages, numérotées de 1 à 7.
Un logimage consiste en une grille de n1 lignes et nc colonnes, dans laquelle
il faut retrouver
une image en noir et blanc à partir de clés. Chaque ligne (et chaque colonne)
est codée par
une liste de clés entières. Un entier m dans une liste de clés correspond à un
bloc de m cases
consécutives à colorier en noir. Par exemple, pour une ligne donnée, la liste
de clés [2,4] signifie
qu'en regardant les cases de cette ligne de gauche à droite, on trouve d'abord
un certain nombre
(éventuellement nul) de cases blanches, puis un bloc de 2 cases noires
consécutives, suivi d'au
moins une case blanche, puis un bloc de 4 cases noires, puis éventuellement des
cases blanches.
De même, la liste de clés pour une colonne décrit la taille des blocs
rencontrés depuis le haut
jusqu'au bas de la colonne. On veut s'assurer que la solution d'un logimage
existe et est unique.
La figure 1 présente un exemple de logimage et sa solution : les listes en
regard des lignes et
des colonnes sont les clés codant la solution. Par exemple, la deuxième ligne
est codée par une
liste de deux clés : [3,1], alors que la première ligne est codée par une liste
contenant une seule
clé : [2].
M] | 15] | 4] } 2]
(2
[3,1]
[4
[4
[1,1]
(a) Exemple de logimage (b) Solution
FIGURE 1: Exemple de logimage et sa solution.
Dans ce sujet, nous représenterons les clés des lignes par une liste de listes
d'entiers, de
même que pour les clés des colonnes. Dans la suite du sujet, cle_1 représentera
la liste des listes
de clés par lignes, et cle_c la liste des listes de clés par colonnes. Dans
l'exemple ci-dessus, on
aura donc cle_1=[[2],1[3,1],[41,{[41,[11,1]]. Une solution sol sera représentée
par une
liste de listes, telle que so1[il][j] représente l'état de la case de la grille
sur la ligne i et la
colonne j. Un état sera codé par un entier : 0 pour une case blanche et 1 pour
une case noire.
Complexité. La complexité, ou le temps d'exécution, d'une fonction F est le
nombre d'opé-
rations élémentaires (addition, multiplication, affectation, test, ...)
nécessaires à l'exécution
de F. Lorsque cette complexité dépend de plusieurs paramètres n et m, on dira
que Fa une
complexité en O(b(n,m)) lorsqu'il existe trois constantes À, no et mo telles
que la complexité
de Fest inférieure ou égale à À - b(n,m), pour tout n > no et m > mo. Lorsqu'il
est demandé
de donner la complexité d'un programme, le candidat devra justifier cette
dernière si elle ne
se déduit pas directement de la lecture du code.
Rappels concernant le langage Python. L'utilisation de toute fonction Python
sur les
listes autre que celles mentionnées dans ce paragraphe est interdite. Si a
désigne une liste en
Python de longueur n :
-- len(a) renvoie la longueur de cette liste, c'est-à-dire le nombre d'éléments
qu'elle contient ;
la complexité de Len est en O(1).
-- ali] désigne le i-ème élément de la liste, où l'indice i est compris entre 0
et len(a) - 1
inclus ; la complexité de cette opération est en O(1).
-- a.append(e) ajoute l'élément e à la fin de la liste a; on supposera que la
complexité de
cette opération est en O(1).
-- a.pop() renvoie la valeur du dernier élément de la liste a et l'élimine de
la liste a; on
supposera la complexité de cette opération en O(1).
-- a.copy() fait une copie de la liste a; la complexité de cette opération est
en O(n).
-- Si f est une fonction, la syntaxe [£f(x) for x in a] permet de créer une
nouvelle liste,
ayant le même contenu que la liste b résultant de l'exécution du code suivant
(et avec la
même complexité) :
b = []
for x in a:
b.append(f(x))
Manipulation de solutions. On pourra utiliser dans la suite les fonctions
suivantes pour
manipuler les solutions :
-- init_sol(nl,nc,v) renvoie une nouvelle solution de n1 lignes et nc colonnes
dont toutes
les cases sont initialisées à v.
-- copy_sol(sol) renvoie une copie de la solution sol obtenue en créant une
nouvelle
solution de même taille et en copiant le contenu de chaque case.
La complexité de chacune de ces fonction est en O(nl x nc).
Convention. Dans le sujet, on supposera que nl et nc (qui décrivent le nombre
de lignes et
le nombre de colonnes de la grille) sont des variables globales accessibles à
l'intérieur de toutes
les fonctions.
Organisation du sujet. Ce sujet comporte quatre parties, de difficulté
croissante. La par-
tie IV dépend des résultats de la partie III, autrement les parties sont
indépendantes.
Partie I -- Préliminaires et vérification d'une solution
Question 1. Ecrire une fonction cases_noires qui prend en argument une liste de
listes
d'entiers cle_1 représentant les clés des lignes d'un logimage et qui renvoie
le nombre de cases
à colorier en noir dans la solution. Quelle est la complexité de cette fonction
?
Question 2. À partir de cle_1l, on peut obtenir le nombre de cases noires de la
solution,
et de même pour cle_c. Écrire une fonction compatibles qui prend ces deux
listes de listes
d'entiers en arguments et qui renvoie True 51 et seulement si elles codent le
même nombre de
Cases noires.
Les règles de construction des logimages imposent au moins une case blanche
entre deux
blocs dans une solution valide. La clé d'une ligne permet de calculer la taille
minimale de la
partie de la ligne occupée par les blocs codés. Par exemple, la clé de ligne
[3,5,2] nécessite
une taille minimale de 12 cases :
Question 3. Ecrire la fonction taille minimale qui prend en argument une liste
(supposée
non vide) d'entiers représentant les clés d'une ligne et renvoie cette taille
minimale.
On souhaite maintenant vérifier si une solution est valide. Pour ceci, on
considère la fonction
verif_ligne ci-dessous, qui a pour but de vérifier que la ligne d'indice i de
la solution sol ne
contient que des 0 et des 1 et contient tous les blocs décrits par cle_1. On
peut s'apercevoir
que cette implémentation contient une erreur, qui sera corrigée à la question
suivante.
1 def verif_ligne(sol, cle_l, i):
2 i_bloc=0
3 taille=0
4 for j in range(nc):
a) couleur _case=sollill;j]
6 if couleur_case=-
7 taille=taille+i
8 if taille>0 and (couleur _case==0 or j+1==nc):
9 if i_bloc 0). On peut exprimer la valeur de M[c] [b] de façon récursive,
en considérant
les deux cas suivants :
1. Si le dernier bloc pouvait être placé sans la dernière case (M[c-11 [b] > 0)
et que la
dernière case n'est pas noire (sol_pli_ligne]l[cl! = 1) alors on peut placer ce
bloc de
la même façon que sans la dernière case, c'est-à-dire M[c] [b] = Mfc-1] [b].
2. Sinon, on regarde si on peut placer le bloc en toute fin de ligne (donc le
faire commencer
à la case d'indice c -- s + 1). Pour ceci, il faut que les deux conditions
ci-dessous soient
réunies :
(a) conflit(c-s+1,s) > c (il n'y a pas de conflit avec les cases de la ligne
jusqu'à c)
(b) Mlc-s-1] [b-1] > 0 (il y a suffisamment de place dans les cases d'indice 0
à c--s--1
pour placer les blocs précédents, afin de laisser une case blanche à l'indice c
-- s)
Si ces deux conditions sont vérifiées, alors on peut placer le bloc en position
EUR -- s +1,
donc Mfc] [b] = c--s+1. Sinon, le dernier bloc ne peut pas être placé : Mc] [b]
= ---1.
Question 10. On suppose que les valeurs de Mc] TO] ont été précalculées pour
tout c. Écrire
un algorithme calcul_matrice qui prend M en argument et qui calcule le reste de
la matrice par
programmation dynamique. La complexité de la fonction devra être en O(nc*) et
sera justifiée.
Dans le cas du premier bloc (b = 0), une attention particulière doit être
portée à la première
case noire de la ligne, si elle existe. On note p la position de la première
case noire si elle existe
(sol_pli_ligne] [p] = 1 et sol_pli_ligne][j] # 1 pour tout j