DS 8 — Concours blanc : plus longue sous-suite commune et graphes d'intervalles¶
MP2I, année scolaire 2021-2022 — concours blanc — durée 4h, tout document ou dispositif électronique interdit.
Corrigé
Pas de corrigé disponible pour ce sujet.
Partie 1 : programmation dynamique¶
Plus longue sous-suite commune¶
En biologie, il est souvent nécessaire de comparer l'ADN provenant d'organismes différents. Un brin d'ADN est une chaîne de molécules appelées bases, où les bases sont l'adénine, la guanine, la cytosine et la thymine. En représentant chaque base par son initiale, un brin d'ADN est une chaîne de caractères sur les lettres A, G, C et T.
Par exemple, un organisme peut avoir comme ADN la chaîne
et un autre
Pour déterminer si deux organismes sont proches, il n'est pas suffisant d'observer les motifs contigus présents dans les deux chaînes car des mutations peuvent avoir modifié le brin en insérant des bases. Cependant, même dans ce cas, il doit être possible de retrouver une partie commune en supprimant des bases. On en déduit la définition suivante :
Définition
Soit \(s = "c_0 \dots c_{n-1}"\) une chaîne de caractères, une chaîne \(s'\) est une sous-chaîne de \(s\) s'il existe un \(p\)-uplet \((i_1, \dots, i_p)\) tel que \(s' = "c_{i_1} \dots c_{i_p}"\) et \(0 \le i_1 < i_2 < \dots < i_p \le n-1\).
Le problème qu'on se pose est de déterminer la plus longue sous-chaîne
commune à deux chaînes. Ainsi,
"GTCGTCGGAAGCCGGCCGAA" est la plus longue
sous-chaîne commune à \(S_1\) et \(S_2\).
Caractérisation des plus longues sous-suites communes¶
Afin de simplifier la programmation d'une solution, on ne va pas s'intéresser à des chaînes de caractères mais à des suites finies d'éléments. On notera ici \((a_0,a_1,\dots,a_{n-1})\) une suite finie de \(n\) éléments. On adapte directement la notion précédente :
Définition
Soit \(A = (a_0, \dots ,a_{n-1})\) une suite finie et \(B\) une autre suite finie. On dit que \(B\) est une sous-suite de \(A\) s'il existe un \(p\)-uplet \((i_1, \dots, i_p)\) tel que \(B = (a_{i_1}, \dots, a_{i_p})\) et \(0 \le i_1 < i_2 < \dots < i_p \le n-1\).
Q1. Si \(A\) est une suite finie de \(n\) éléments, combien comporte-t-elle de sous-suites ? Que penser d'un algorithme qui, pour déterminer une plus longue sous-suite commune entre \(A\) et \(B\), itère sur les sous-suites de \(A\) et teste si elles sont sous-suites de \(B\) ?
Soit \(A = (a_0, \dots, a_{n-1})\) et \(i \in \range{0}{n}\), on note \(A_i = (a_0, \dots, a_{i-1})\) le \(i\)-ème préfixe de \(A\). Par définition, \(A_0\) est la suite finie vide \(()\) et \(A_n = A\).
Q2. Soient \(A = (a_0, \dots, a_{m-1})\) et \(B = (b_0, \dots, b_{n-1})\) deux suites finies et \(C = (c_0, \dots, c_{p-1})\) une plus longue sous-suite commune à \(A\) et \(B\).
Montrer que :
- Si \(a_{m-1} = b_{n-1}\) alors \(c_{p-1} = a_{m-1} = b_{n-1}\) et \(C_{p-1}\) est une plus longue sous-suite commune à \(A_{m-1}\) et \(B_{n-1}\).
- Si \(a_{m-1} \neq b_{n-1}\) alors \(c_{p-1} \neq a_{m-1}\) entraîne que \(C\) est une plus longue sous-suite commune à \(A_{m-1}\) et \(B\).
- Si \(a_{m-1} \neq b_{n-1}\) alors \(c_{p-1} \neq b_{n-1}\) entraîne que \(C\) est une plus longue sous-suite commune à \(A\) et \(B_{n-1}\).
Équation de récurrence¶
On considère ici deux suites \(A = (a_0, \dots, a_{m-1})\) et \(B = (b_0, \dots, b_{n-1})\) et, pour \(i \in \range{0}{m}\) et \(j \in \range{0}{n}\), on note \(f(i,j)\) la longueur d'une plus grande sous-suite commune à \(A_i\) et \(B_j\).
Q3.
- Calculer \(f(i,j)\) quand \(i = 0\) ou \(j = 0\).
- On suppose que \(i, j > 0\) et \(a_{i-1} = b_{j-1}\). Justifier que \(f(i,j) = 1 + f(i-1, j-1)\).
- On suppose que \(i, j > 0\) et \(a_{i-1} \neq b_{j-1}\). Justifier que \(f(i,j) = \max(f(i, j-1),f(i-1,j))\).
Résolution de la récurrence¶
On va résoudre la récurrence en OCaml ici.
Les sous-suites sont ainsi représentées par des 'a array.
Q4. Écrire une fonction naïve longueur_sous_suite
de signature
'a array -> 'a array -> int -> int -> int
telle que l'appel à longueur_sous_suite a b i j
renvoie la valeur de \(f(i,j)\) pour les suites données par \(a\) et \(b\).
On rappelle l'existence des fonctions suivantes :
Hashtbl.create nqui crée une table de hachage de taille initiale \(n\).Hashtbl.find t cqui renvoie la valeur associée à la clécdans la table et produit l'exceptionNot_founds'il n'y en a pas.Hashtbl.add t c vqui rajoute une correspondance entre la clécet la valeurv.
Q5. Écrire une version mémoïsée de la fonction précédente.
On pourra, au choix, créer un cache en dehors de la fonction ou dans la fonction elle-même.
Tabulation¶
On va considérer ici une version tabulée en C.
On va représenter \(f\) dans une matrice de dimension \((m+1) \times (n+1)\).
Pour simplifier, on peut considérer un tableau M de \((m+1)(n+1)\) entiers
où on place \(f(i,j)\) dans M[j * (m+1) + i].
Q6. Déterminer un ordre de remplissage de \(M\) qui soit compatible avec la relation de récurrence établie au paragraphe précédent.
Q7. Écrire une fonction de prototype
int longueur_sous_suite(int *a, int m, int *b, int n);
qui renvoie la longueur d'une plus longue sous-suite commune aux deux tableaux
d'entiers a de longueur m et b de longueur n.
On utilisera une tabulation selon le principe qui vient d'être exposé.
Partie 2 : problème sur les graphes¶
I. Graphes d'intervalles¶
On considère le problème concret suivant : certains cours doivent avoir lieu dans un intervalle de temps précis (par exemple de 8h à 9h50) et on cherche à attribuer une salle à chaque cours. On souhaite évidemment qu'à tout moment une salle ne puisse pas être attribuée à deux cours différents et on aimerait utiliser le plus petit nombre de salles possible.
Ce problème d'allocation de ressources (ici les salles) en fonction de besoins fixes (ici les horaires des cours) intervient dans de nombreuses situations très diverses (allocation de pistes d'atterrissage aux avions, répartition de la charge de travail sur plusieurs machines, etc.).
Représentation du problème¶
On modélise le problème ainsi :
- chaque besoin est représenté par un segment \([a, b]\) où \(a,b\in \N\) et \(a\leq b\) ;
- deux besoins \(I\) et \(J\) sont en conflit quand \(I\cap J\neq \emptyset\).
La donnée du problème est une suite finie \((I_0,\dots,I_{n-1})\) de \(n\) segments où \(n\in \N\).
Q8. Dans le problème \((b)\) quels sont les intervalles en conflit avec \(I_5\) ?
On représente un segment en OCaml par un couple d'entiers, la
donnée du problème est représentée par un tableau d'intervalles,
de type (int * int) array. Dans toute la suite, on supposera
toujours que les intervalles sont des segments bien formés et donc que
si (a, b) : int * int représente un intervalle alors on a a <= b
et cela représente toujours le segment \([a, b]\).
Le problème \((a)\) de la figure 1 est ainsi représenté par le tableau
[|(0, 3); (1, 3); (2, 5); (4, 7);
(6, 10); (8, 9); (11, 12)|]
On cherche à écrire une fonction conflit de type int * int -> int * int -> bool
qui renvoie true si et seulement si deux
segments passés en paramètres sont en conflit. Voici plusieurs
versions proposées par plusieurs élèves.
let conflit1 (a, b) (c, d) = (a <= c) && (c <= b)
let conflit2 (a, b) (c, d) = (a < d) && (c < b)
let conflit3 (a, b) (c, d) = not ((b < c) || (d < a))
let conflit4 (a, b) (c, d) =
((a <= c) && (d <= b)) || ((c <= a) && (b <= d))
Q9. Une seule de ces versions est correcte. Laquelle ?
On suppose par la suite disposer de la version correcte de cette
fonction conflit : int * int -> int * int -> bool.
Graphe simple non orienté¶
On rappelle qu'un graphe simple non orienté, que l'on appellera par la suite simplement graphe, est un couple \(G = (S, A)\) où
- \(S\) est un ensemble fini dont les éléments sont appelés les sommets du graphe (ici \(S\) peut être vide) ;
- \(A\) est un ensemble de paires (donc d'éléments distincts) de \(S\). Lorsque \(\{x,y\}\in A\) on dit que \(x\) et \(y\) sont reliés dans \(G\) et \(\{x,y\}\) est appelée une arête de \(G\). Les sommets reliés à un sommet \(x\) sont appelés les voisins de \(x\).
Étant donnée une énumération de \(S\) sous la forme d'une suite
\((x_0,\dots,x_{n-1})\) on représente l'ensemble \(A\) en OCaml par
un élément de type aretes : int list array. Ainsi pour \(i\in
\{0,\dots,n-1\}\), la liste aretes.(i) contient les indices \(j\)
tels que \(x_i\) soit relié à \(x_j\) dans \(G\).
Q10. Comment s'appelle cette représentation d'un graphe ?
On représente graphiquement le graphe \(G\) par un diagramme où les arêtes sont représentées par des traits entre les sommets représentés par des cercles.
Les arêtes du graphe dont une représentation graphique est donnée à la figure 3 sont représentées en Caml par le tableau :
[|[1; 2; 3]; [0; 2; 3]; [0; 1; 3; 4]; [0; 1; 2]; [2]|]
Un tel tableau de listes d'arêtes suffit pour déterminer un graphe lorsque l'énumération des sommets est connue car on peut alors identifier un sommet à son indice. Dans la suite de ce problème on identifiera ainsi un graphe à son tableau de listes d'arêtes.
Graphe d'intervalles¶
Soit \(\overline{I}=(I_0,\dots,I_{n-1})\) une suite finie de segments, on appelle graphe d'intervalles associé à \(\overline{I}\) le graphe \(G(\overline{I})\)
- dont les sommets sont les segments \(I_0,\dots,I_{n-1}\)
- et où, pour \(i,j\in \{0,\dots,n-1\}\), avec \(i\neq j\) les sommets \(I_i\) et \(I_j\) sont reliés si et seulement s'ils sont en conflit.
Par exemple, le graphe d'intervalles qui correspond au problème (a) de la figure 1 admet la représentation graphique de la figure 4.
Q11. Donner une représentation graphique du graphe d'intervalles pour le problème \((b)\) de la figure 2.
Q12. Écrire en OCaml une fonction construit_graphe de type
(int * int) array -> int list array qui étant donné un
tableau segments des segments
\(\overline{I}=(I_0,\dots,I_{n-1})\), énumérés dans cet ordre,
renvoie la représentation des arêtes de \(G\). Quelle est sa
complexité ?
II. Coloration d'un graphe¶
Soit \(G = (S, A)\) un graphe simple non orienté dont les sommets sont donnés par une énumération \(x_0,\dots,x_{n-1}\). On appelle coloration de \(G\) (associée à cette énumération) une suite finie d'entiers naturels \((c_0,\dots,c_{n-1})\) telle que
L'entier \(c_i\) est appelé la couleur du sommet \(x_i\) et la condition se comprend ainsi : deux sommets reliés ont des couleurs distinctes. Dorénavant, le terme couleur sera synonyme d'entier naturel. Par exemple, la suite finie \((0, 1, 2, 3, 0)\) est une coloration du graphe de la figure 3, utilisant \(4\) couleurs.
Lorsqu'une coloration utilise le plus petit nombre de couleurs distinctes possibles, on dit qu'elle est optimale. On note alors \(\chi(G)\) ce nombre minimum de couleurs, appelé le nombre chromatique de \(G\).
Remarque
En associant une salle à chaque couleur, on peut répondre au problème initial à l'aide d'une coloration de son graphe d'intervalles associé.
Q13. Déterminer \(\chi(G)\) pour les deux graphes d'intervalles associés aux problèmes \((a)\) de la figure 1 et \((b)\) de la figure 2. Justifier rigoureusement.
Couleurs disponibles¶
Q14. Quelle est la complexité temporelle de la fonction List.mem ?
Q15. Écrire en OCaml une fonction plus_petit_absent : int list -> int telle que l'appel
plus_petit_absent liste sur une
liste d'entiers naturels liste renvoie le plus petit entier
naturel non présent dans liste. Justifier que cette fonction
termine. Une fonction de complexité quadratique en la taille de la
liste dans le pire cas convient ici.
Q16. Expliquer précisément comment on pourrait réaliser la fonction précédente avec une complexité linéaire en la taille de la liste dans le pire cas. On ne demande pas d'écrire la fonction en OCaml mais d'en décrire précisément l'idée.
Par la suite on suppose que la fonction plus_petit_absent réalise
cette complexité linéaire dans le pire cas.
On considère une coloration progressive des sommets d'un graphe.
Pour cela, une coloration partielle est un tableau couleurs : int array
tel que couleurs.(i) contient la couleur de \(i\) s'il est
coloré et \(-1\) sinon, ce qui ne pose pas de problème car les
couleurs sont toujours positives.
Q17. Écrire en OCaml une fonction couleurs_voisins de
signature int list array -> int array -> int -> int list telle
que l'appel couleurs_voisins aretes couleurs i renvoie, avec
les doublons éventuels et dans un ordre quelconque, la liste des
couleurs des voisins colorés du sommet d'indice \(i\) dans le graphe
décrit par aretes où le tableau couleurs décrit une
coloration partielle. Quelle est sa complexité ?
Q18. En déduire une fonction couleur_disponible : int list array -> int array -> int -> int
de telle manière à ce que l'appel
de fonction couleur_disponible aretes couleurs i renvoie la
plus petite couleur pouvant être attribuée au sommet \(i\) afin
qu'il n'ait la couleur d'aucun de ses voisins dans le graphe
décrit par aretes. Quelle est sa complexité ?
Colorations et cliques¶
Soit \(G = (S, A)\) un graphe. Un sous-ensemble \(C \subseteq S\) est appelé une clique de \(G\) lorsqu'il vérifie
La taille d'une clique est son nombre d'éléments. La taille de la plus grande clique de \(G\) (celle qui possède le plus d'éléments) est notée \(\omega(G)\).
Q19. Déterminer, sans justifier, \(\chi(G)\) et \(\omega(G)\) pour un graphe \(G = (S, A)\) avec \(|S| = n > 0\) lorsque
- (a) \(G\) ne possède pas d'arête (tous les sommets sont isolés) ;
- (b) \(G\) est un graphe complet à \(n\) sommets ;
- (c) \(G\) est un cycle à \(n\) sommets.
Q20. Comparer \(\chi(G)\) et \(\omega(G)\) pour un graphe \(G\) quelconque. Justifier.
III. Algorithme glouton pour la coloration d'intervalles¶
Étant donnée une liste de segments \(\overline{I}=(I_0,\dots,I_{n-1})\) de longueur \(n\geq 1\), on se propose de déterminer une coloration optimale de son graphe d'intervalles associé. On appelle coloration de \(\overline{I}\) une suite finie d'entiers naturels \((c_0,\dots,c_{n-1})\) telle que
On suppose dans cette partie que les segments \(I_k=[a_k,b_k]\), pour \(k\in \{0,\dots,n-1\}\), sont énumérés dans l'ordre croissant de leurs extrémités gauches, c'est-à-dire que
On propose l'algorithme suivant :
Pour \(k\) variant de \(0\) à \(n-1\), colorer l'intervalle \(I_k\) avec la plus petite couleur non encore utilisée dans la coloration des intervalles \(I_j\), avec \(0\leq j<k\), qui ont une intersection non vide avec \(I_k\).
Ainsi, l'intervalle \(I_0\) est toujours coloré avec la couleur 0, l'intervalle \(I_1\) reçoit la couleur \(0\) si \(I_0\cap I_1=\emptyset\), et la couleur \(1\) sinon, etc.
L'algorithme sur un exemple¶
Q21. Déterminer la coloration renvoyée par cet algorithme pour le problème \((b)\) décrit sur la figure 2, pour l'énumération des intervalles indiquée (qui respecte bien la contrainte demandée).
Coloration¶
Q22. Écrire en OCaml une fonction coloration : int list array -> int array
telle que l'appel coloration aretes, où
aretes représente les arêtes du graphe d'intervalles
associé à une liste de segments triés par ordre croissant de
leurs extrémités gauches, renvoie la coloration obtenue avec
l'algorithme ci-dessus.
Q23. Déterminer très soigneusement la complexité de la fonction précédente en fonction du nombre \(n\) d'intervalles et du nombre \(m\) d'arêtes du graphe d'intervalles associé à la liste \(\overline{I}\).
Preuve de l'algorithme¶
On se propose maintenant de démontrer que l'algorithme ci-dessus fournit une coloration optimale de l'ensemble des segments. Soit \(k\) un entier entre \(0\) et \(n-1\). On suppose qu'à la \(k\)-ième étape de l'algorithme, le segment \(I_k = [a_k, b_k]\) reçoit la couleur \(c\).
Q24. Montrer que \(I_k\) a au moins \(c\) voisins d'indices inférieurs à \(k\), c'est-à-dire parmi \(I_0,\dots,I_{k-1}\).
Q25. Montrer que l'ensemble constitué de \(I_k\) et de ses voisins d'indice inférieur à \(k\) constitue une clique de taille au moins \(c+1\) dans le graphe d'intervalles associé. On pourra raisonner sur l'extrémité gauche \(a_k\) du segment \(I_k\).
Q26. En déduire que le nombre de couleurs nécessaires à une coloration de l'ensemble des segments est au moins égal à \(c+1\).
Q27. Conclure.
IV. Coloration gloutonne¶
L'algorithme de coloration étudié dans la partie précédente s'applique plus généralement à tout graphe étant donné une énumération \((x_0,\dots,x_{n-1})\) des sommets :
pour \(i\) allant de \(0\) à \(n-1\), on colore \(x_i\) avec la plus petite couleur qui ne soit pas utilisée par un de ses voisins déjà colorés.
On appelle coloration gloutonne d'un graphe \(G = (S, A)\) une coloration obtenue par application de l'algorithme ci-dessus, ce qui dépend uniquement de l'énumération des sommets choisie. Ainsi, si \(|S| = n\), chacune des \(n!\) façons d'ordonner les sommets du graphe induit une coloration gloutonne des sommets du graphe.
Q28. Montrer qu'une coloration gloutonne n'est pas toujours optimale. On donnera un contre-exemple sous la forme d'un graphe, d'une énumération de ses sommets, de la coloration gloutonne associée et du nombre chromatique de ce graphe (qui ne sera donc pas le même que le nombre de couleurs utilisées par la coloration gloutonne).
Q29. Montrer qu'il existe des colorations optimales qui ne sont pas des colorations gloutonnes. On donnera à nouveau un contre-exemple sous la forme d'un graphe et d'une coloration associée et on justifiera rapidement que cette coloration ne saurait être une coloration gloutonne.
L'algorithme de coloration glouton pour une énumération des sommets ne donne pas, en général, une solution optimale. Cependant, il existe toujours une énumération des sommets qui fournit une coloration gloutonne optimale.
Propriété
Soit un graphe \(G = (S, A)\). Il existe une énumération \((x_0,\dots,x_{n-1})\) des sommets de \(G\) telle que la coloration gloutonne calculée à partir de cet ordre soit optimale.
On se propose de démontrer cette proposition. Considérons une coloration valide du graphe \(G\) utilisant \(k\) couleurs numérotées de \(0\) à \(k - 1\) et notons, pour \(i \in \{0, \dots, k - 1\}\), \(C_i\) l'ensemble des sommets de \(G\) auxquels a été affectée la couleur \(i\) dans cette coloration. Considérons une énumération des sommets de \(G\) qui respecte l'ordre sur les classes \(C_i\), c'est-à-dire telle que si \(0 \leq i < j \leq k - 1\), tout sommet de couleur \(i\) est classé avant tout sommet de couleur \(j\). Considérons maintenant la coloration gloutonne obtenue avec cet ordre.
Q30. Montrer que, pour \(i \in \{0, \dots, k - 1\}\), lorsque l'algorithme glouton affecte une couleur \(c\) à un sommet \(x\) de la classe \(C_i\), cette couleur \(c\) ne peut être qu'inférieure ou égale à \(i\).
Q31. Montrer que cette coloration gloutonne utilise au plus \(k\) couleurs.
Q32. Conclure.
Il suffit donc de calculer les colorations gloutonnes associées à toutes les énumérations possibles des sommets pour trouver le nombre chromatique d'un graphe.
Q33. Que pensez-vous de cette approche ?
Trouver un ordre sur les sommets d'un graphe tel que la coloration gloutonne associée à cet ordre soit optimale est un problème difficile que l'on ne sait pas résoudre de manière efficace, du moins dans le cas général.
V. Graphes munis d'un ordre d'élimination parfait¶
On introduit ici la notion d'ordre d'élimination parfait, dont on montre qu'il existe toujours pour un graphe d'intervalles, et pour lequel l'algorithme glouton donne toujours une solution optimale.
Soient \(G = (S, A)\) un graphe et \((x_0,\dots,x_{n-1})\) une énumération des sommets de \(G\). Pour tout \(i\in \{0,\dots,n-1\}\), on note \(G_i=(S_i,A_i)\) où \(S_i=\{x_0,\dots,x_i\}\) et
\(G_i\) est ainsi le graphe déduit de \(G\) en se restreignant aux sommets de \(x_0\) à \(x_i\).
Une énumération \((x_0,\dots,x_{n-1})\) des sommets de \(G\) est appelée un ordre d'élimination parfait si pour tout \(i\in \{0,\dots,n-1\}\), les voisins de \(x_i\) d'indices inférieurs à \(i\) (c'est-à-dire apparaissant avant \(i\) dans l'énumération) forment une clique (c'est-à-dire sont tous reliés).
Par exemple, dans la figure 5, l'énumération \((a,b,c,d,e,f)\) est bien un ordre d'élimination parfait alors que \((b,c,e,d,f,a)\) ne l'est pas puisque \(d\) y est précédé de \(e\) et \(c\) qui sont tous deux voisins de \(d\) sans être reliés.
Un exemple¶
Q34. Déterminer, sans justifier, parmi les exemples proposés lesquels sont bien des ordres d'élimination parfaits pour le graphe \(G\) donné par la figure 6 :
- (a) \((a,b,c,d,e,f,g,h)\).
- (b) \((a,c,b,d,f,g,h,e)\).
- (c) \((a,b,e,c,f,h,g,d)\).
- (d) \((b,c,e,f,a,g,d,h)\).
Q35. Montrer qu'il existe des graphes qui n'admettent aucun ordre d'élimination parfait. On donnera un contre-exemple sous la forme d'un graphe et l'on justifiera rapidement.
Validation d'un ordre d'élimination parfait¶
Dans cette partie, les fonctions devront être écrites en C. Pour
cela, un graphe à \(n\) sommets et de degré maximum \(d\) sera représenté
par un tableau statique g à \(n\) lignes et \(d+1\) colonnes où
chaque ligne i du tableau g est un tableau g[i]
contenant dans sa première case g[i][0] le degré \(d_i\) du sommet
\(i\), puis, dans les cases indices \(1\) à \(d_i\), et dans un ordre
arbitraire, les indices des voisins de \(i\). On n'accèdera donc pas aux
cases d'indices strictement supérieurs à \(d_i\) pour un sommet \(i\), qui
peuvent contenir une valeur non initialisée.
Par exemple, le graphe de la figure 3 peut être représenté par :
int g[5][4] = {
{3, 1, 2, 3},
{3, 0, 3, 2},
{3, 0, 1, 3},
{3, 0, 1, 2},
{1, 2}
};
On remarquera qu'ici encore, une fois que la représentation d'un graphe est choisie, elle implique une énumération des sommets dont on cherchera ici à déterminer si elle correspond ou non à un ordre d'élimination parfait.
Étant donné un graphe \(G = (S ,A)\) et un ensemble \(S'\subseteq S\) de sommets de \(G\), le sous-graphe de \(G\) induit par \(S'\) est le graphe \(H = (S',A')\) où \(A'\subseteq A\) est l'ensemble des arêtes de \(G\) dont les extrémités appartiennent à \(S'\). On pourra identifier le sous-graphe \(H = (S',A')\) avec l'ensemble de sommets \(S'\).
Un graphe \(G = (S, A)\) à \(|S| = n\) sommets est représenté en C par un
tableau bidimensionnel g. On représente en C un
sous-graphe induit \(H = (S',A')\) d'un graphe \(G = (S, A)\) possédant
\(n\) sommets par la donnée du graphe d'origine g et d'un tableau
de booléens sommets à \(n\) cases dont la case \(i\) vaut true
si et seulement si le sommet d'indice \(i\) est un sommet du sous-graphe
induit et false sinon. On représente de même un sous-ensemble \(S'
\subseteq S\) de sommets du graphe par un tableau de booléens de taille
\(n\) indiquant pour chaque sommet de \(S\) sa présence dans \(S'\) ou non.
Attention
Dans toute cette partie \(n\) et \(d\) seront des constantes globales.
Q36. Écrire en C une fonction de type void voisins_inferieurs(int g[n][d + 1], int x, bool voisins_inf[n])
telle que l'appel voisins_inferieurs(g, x, voisins_inf) met à
jour le tableau de booléens voisins_inf pour qu'il représente
l'ensemble des voisins du sommet x d'indices strictement
inférieurs à celui de x. On ne présupposera rien sur le
remplissage initial du tableau voisins_inf. On garantira une
complexité linéaire en \(|S| = n\).
Q37. Écrire en C une fonction de type bool est_clique(int g[n][d + 1], bool sommets[n])
telle que l'appel
est_clique(g, sommets) renvoie true si et seulement si
l'ensemble de sommets représenté par le tableau de booléens
sommets forme une clique de g, c'est-à-dire que le
sous-graphe de g engendré par ces sommets est complet. On
garantira une complexité linéaire en \(|S| + |A|\).
Indication : calculer en temps linéaire la taille et le nombre
d'arêtes du sous-graphe.
Q38. Écrire en C une fonction de signature bool est_ordre_parfait(int g[n][d + 1])
telle que
est_ordre_parfait(g) renvoie true si et seulement si
l'énumération associée au graphe représenté par g est
un ordre d'élimination parfait. Quelle est sa complexité ?
Ordre d'élimination parfait pour un graphe d'intervalles¶
Q39. Justifier rapidement qu'un graphe d'intervalles admet un ordre d'élimination parfait. Indication : on en a explicité un dans la partie III.
Ordre d'élimination parfait¶
Un sommet \(v\) d'un graphe \(G\) est dit simplicial lorsque l'ensemble des voisins de \(v\) dans \(G\) est une clique.
Q40. Quels sont les sommets simpliciaux du graphe de la figure 5 ?
Q41. Écrire une fonction de type bool simplicial(int g[n][d + 1], bool sg[n], int k)
telle que l'appel simplicial(g, sg, k), où le
sommet d'indice \(k\) est supposé appartenir au sous-graphe induit
\(H\) décrit par (g, sg), renvoie true si le sommet
d'indice \(k\) est simplicial dans \(H\) et false sinon.
Déterminer la complexité de la fonction simplicial.
Q42. Écrire une fonction int trouver_simplicial(int g[n][d + 1], bool sg[n])
telle que trouver_simplicial(g, sg) renvoie,
s'il en existe, un sommet simplicial du sous-graphe induit décrit
par (g, sg) et sinon renvoie l'entier relatif \(-1\).
Déterminer la complexité de cette fonction.
Q43. Montrer que si un graphe \(G = (S, A)\) possède un ordre d'élimination parfait, alors pour tout sommet simplicial \(x\) de \(G\), le sous-graphe \(H = (S', A')\) engendré par \(S' = S \setminus \{x\}\) en possède également un.
Q44. Écrire une fonction int *ordre_parfait(int g[n][d + 1])
telle que l'appel ordre_parfait(g) renvoie un ordre
d'élimination parfait du graphe décrit par g, s'il en
existe un et NULL sinon. Déterminer la complexité de la
fonction ordre_parfait.