Contrôle sur papier INF441 Modex Programmation efficace juin 2010 1. Photo de dés Plusieurs dés ont été lancés sur une table, et une photo a été pris de haut. Cette photo est représentée par une matrice. Chaque pixel a une parmi trois couleurs : la couleur de fond, la couleur d un dé, et la couleur d un point sur un dé. Deux pixels au coordonnées (i, j) et (i, j ) sont adjacents si i i + j j = 1. Un ensemble S de pixels est connecté si pour toute paire (p, q) de pixels dans S, il existe une séquence p 1, p 2,..., p k dans S, tel que p = p 1 et q = p k et p i est adjacent à p i+1 pour tout 1 i < k. Nous considérons un dé comme un ensemble connecté maximal composé seulement de pixels qui ne sont pas de la couleur de fond. Connecté maximal veut dire qu on ne peut pas ajouter un autre pixel qui ne serait pas de la couleur de fond, sans déconnecter l ensemble. De la même manière on considère un point comme un ensemble connecté maximal de pixels de la couleur d un point. L entrée constitue une image d un lancer de plusieurs dés. La première ligne contient deux entiers w et h, la largueur et hauteur respective de l image, avec 10 w, h 50. Les h lignes suivantes contiennent w caractères chacune. Les caractères peuvent être. pour la couleur de fond, * pour la couleur d un dé, et X pour la couleur d un point. Les dés peuvent avoir plusieurs tailles et ne pas être complètement carrés par déformation optique. L image contient au moins un dé et le nombre de points par dé est entre 1 et 6 inclu. La sortie devrait être une seule ligne contenant les nombres de points par dés en ordre croissants et séparés par des espaces. Décrivez un algorithme qui résoud ce problème en temps O(wh). Cette description pourra utiliser des primitives comme calculer un plus court chemin par Dijkstra, ou résoudre une formule 2-SAT, sans détailler ces primitives. Et ce n est pas grave si vous ne vous souvenez plus exactement les noms des méthodes des classes de la bibliothèques, comme les vecteurs, les files, les dictionaires, etc. 1
1.0.1. Exemple entrée 30 15.........*......*****...****......*X***...**X***......*****...***X**......***X*...****......*****...*.........***...******......**X****...*X**X*......*******...******......****X**...*X**X*......***...******...... 1.0.2. Sortie correspondante 1 2 2 4 2. Median Jean organise une fête. Régulièrement pendant la fête il lui faut un volontaire pour ouvrir les bouteilles de vin. Il n aime pas les règles du type c est au plus jeune de faire cette tâche, ou c est au plus vieux de faire cette tâche. Donc il aimerait que ça soit une personne à l age médian qui ouvre les bouteilles. Concrètement si les ages des personnes présentes à la fête sont a 0 a 1... a n 1, alors l age médian est a n/2. En début de soirée seul Jean et son amie sont présents, et leur ages sont 41 et 47. Ensuite pendant la soirée deux types d évènement peuvent arriver. Soit une nouvelle personne arrive, soit une nouvelle bouteille arrive et il faut trouver l age median. Décrivez en pseudo-code un algorithme qui lit n = 100.000 lignes. Si une ligne commence par la lettre a, alors elle est suivit d un espace et d un entier, qui indique l age d un nouvel arrivant. Si une ligne commence par b, il faut afficher le median des ages présents à la fête. Il n y a pas d autre type de ligne. Vous n avez pas la promesse que l age est borné par une constante, néamoins il tient dans un entier. Ce n est pas très réaliste, mais sinon le problème devient trivial. Votre algorithme devra avoir une complexité de O(n log n), donc de l ordre de O(log n) par ligne. 2
3. Ensembles sans disputes Les enfants s entendent mal avec leur parents, c est bien connu. Par contre ils s entendent bien avec leurs grand-parents, ou leurs frères et sœurs. Étant donnée un arbre T (V, E), trouvez un sous-ensemble S T qui soit de cardinalité maximale tel que pour tout u, v S on ait (u, v) E. L arbre n est pas forcément binaire. Décrivez un algorithme qui résoud ce problème en temps linéaire. 4. Erreurs bêtes 4.1. Pourquoi est-ce que le programme suivant produit RuntimeError sur le juge? import java.util.*; /* Lire 9 entiers et afficher le median */ class Main { public static void main(string args[]) { Scanner in = new Scanner(System.in); int[] tab; for (int i=0; i<9; i++) t[i] = in.nextint(); Arrays.sort(t); System.out.println(tab[4]); 4.2. Pour est-ce que le programme suivant produit Time-Limit-Exceeded sur le juge? Ce programme doit calculer les composantes connexes d un graphe de n 1000 sommets. Vous pouvez supposer que n et M sont correctement intialisées par la méthode main, et qu elle appèle nbcomp. boolean[] mark; int n; boolean [][]M; // marques de visite pour DFS // nombre de sommets // matrice d adjacence n*n symmetrique /* faire un parcours DFS */ void dfs(int u) { // visiter tous les voisin v de u for (int v=0; v<n; v++) if (M[u][v] &&!mark[v]) 3
dfs(v); /* calculer le nombre de composantes connexes */ int nbcomp() { int c = 0; Arrays.fill(mark, false); for (int u=0; u<n; u++) { if (!mark[u]) { c++; // nouvelle composante trouv ee dfs(u); return c; 4
A. Corrigé A.1. Photo de dés Visiblement c est un problème de composantes connexes dans un graphe. Pour pouvoir réutiliser une même fonction de détection des composantes connexes, on va utiliser des structures de données particulières. D abord on va stocker la photo dans une matrice int [][]P ou int P[50][50] en C++ avec le codage par pixel : 0=fond, 1=dé, 2=point. Ainsi on peut utiliser un masque pour la fonction d exploration dfs. Si le masque est 1 2 c est à dire 3 on veut explorer le graphe composé de tous les sommets différent du fond. Si le masque est 2 on veut explorer seulement les pixels d un point. Dans un premier passage on marque tous les pixels hors-fond du numéro de leur composante connexe. Ainsi quand on trouvera un point, on saura à quel dé il appartient. Ensuite dans un deuxième passage on calcule les composantes connexes composés seulement des points, et on augmente le nombre de points du dé correspondant. // modex programmation efficace ecole polytechnique c.durr 2010 / The Die Is Cast http://acm.tju.edu.cn/toj/showp1605.html / import java.util. ; import java.awt. ; class Main { static int w,h; static int [][] P, C; // couleur pixel et numero de de static boolean [][]v; // marque pour visite static int []N; // nb de points par de static final int di[] = { 0,+1, 1, 0; // 4 voisinage static final int dj[] = {+1, 0, 0, 1; static void dfs(int i, int j, int mask, int c) { v[i][j] = true; C[i][j] = c; for (int k=0; k<4; k++) { int i2 = i+di[k]; int j2 = j+dj[k]; if (0<= i2 && i2<h && 0<= j2 && j2<w &&!v[i2][j2] && (P[i2][j2] & mask)!= 0) dfs(i2,j2,mask,c); 5
public static void main(string args[]) { Scanner in = new Scanner(System.in); for (int test=1; true; test++) { w = in.nextint(); h = in.nextint(); if (w==0) break; P = new int[h][w]; C = new int[h][w]; v = new boolean[h][w]; // lire l image for (int i=0; i<h; i++) { String line = in.next(); int j=0; for (char c: line.tochararray()) { int p=0; if (c== ) p=1; if (c== X ) p=2; P[i][j++] = p; // detecter les des int c=0; // nb de des trouves for (int i=0; i<h; i++) for (int j=0; j<w; j++) if (!v[i][j] && (P[i][j]&3)!=0) dfs(i,j, 3, c++); N = new int[c]; // nb points par de // detecter les points v = new boolean[h][w]; // enlever les marques for (int i=0; i<h; i++) for (int j=0; j<w; j++) if (!v[i][j] && P[i][j]==2) { N[C[i][j]]++; dfs(i,j, 2, 0); 6
// afficher dans l ordre Arrays.sort(N); System.out.println( Throw + test); int l=0; for (int ni: N) { if (l++>0) // espace separateur pas en premier System.out.print( ); System.out.print(ni); System.out.println(); System.out.println(); A.2. Median Voici une solution avec deux files de priorité. Le médian divise les ages en deux parties. On va stocker chacunes des parties dans une file de priorité. La file L, stocke les jeunes, avec la convention que L.pop() retourne le plus vieux, et la file H stocke les vieux, avec la convention que H.pop() retourne le plus jeune. En pratique il faudra soit munir la file L avec un comparateur adapté, soit multiplier tout son contenu par 1, pour qu on puisse déterminer l élément maximum de L par L.top(). On se débrouille pour que L = H ou L = H 1. Ainsi le median est toujours H.top(). Quand un nouvel age a arrive, on compare avec le médian et on ajoute en L si a < H.top() et en H sinon. Puis on doit reéquilibrer : Si jamais L > H, on enleve le plus vieux de L pour le rajouter en H. Et si jamais L < H 1, on enleve le plus jeune de H pour le rajouter en L. Une autre possibilité est de maintenir un arbre équilibré de recherche, et un itérateur sur le médian. Cette solution fonctionne en C++ où il existe la classe multiset et qu un itérateur reste valide après un ajout, contrairement à Java. A.3. Ensembles sans disputes Il s agit de trouver le plus grand ensemble indépendant dans un graphe. Dans un arbre c est un problème facile. Tout simplement pour chaque sous-arbre enraciné en v on note e + (v) la cardinalité du plus grand ensemble indépendant contenant v, et e (v) la cardinalité du plus grand ensemble indépendant ne contenant pas v. Alors pour les feuilles e + (v) = 1 et e (v) = 0. 7
Pour les autres sommets e + (v) = 1 + u e (u), où la somme est sur tous les fils de v. Et e (v) = u max{e+ (u), e (u). A.4. Runtime error Il manque l initiation du tableau avec tab = new int[9];. A.5. Timelimit Exceeded Il manque la pose de la marque dans dfs, donc le programme boucle indéfiniment. 8