Contrôle Classant d'Informatique

Promotion 1998

Corrigé rédigé par Georges Gonthier et François Morain
Question 1.
a)
static int distance(int x1, int y1, int x2, int y2){
  return Math.abs(x2-x1)+Math.abs(y2-y1);
}
b)
static ListeM estDansListe(ListeM lm, int num){
  if(lm == null) return null;
  if(lm.num == num) return lm;
  else return estDansListe(lm.suiv, num);
}
c)
static int distanceM(ListeM lm, int num1, int num2){
  ListeM l1, l2;

  l1 = estDansListe(lm, num1);
  l2 = estDansListe(lm, num2);
  return distance(l1.xm, l1.ym, l2.xm, l2.ym);
}
Question 2.
a)
static int plusPetiteDistance(ListeM lm, int x, int y){
  int dmin, d;

  if(lm == null) return -1;
  dmin = distance(x, y, lm.xm, lm.ym);
  for(ListeM l = lm.suiv; l != null; l = l.suiv){
    d = distance(x, y, l.xm, l.ym);
    if(d < dmin) dmin = d;
  }
  return dmin;
}
b)
static ListeM plusProches(ListeM lm, int x, int y){
  int dmin = plusPetiteDistance(lm, x, y);
  ListeM lpp = null;

  for(ListeM l = lm; l != null; l = l.suiv){
    if(distance(x, y, l.xm, l.ym) == dmin)
      lpp = new ListeM(lpp, l.xm, l.ym, l.num);
  }
  return lpp;
}
Question 3.
a)
static int distanceM2(ListeM[] reg, int num1, int num2){
  ListeM l1, l2;

  l1 = estDansListe(reg[h(num1)], num1);
  l2 = estDansListe(reg[h(num2)], num2);
  return distance(l1.xm, l1.ym, l2.xm, l2.ym);
}
b) On doit parcourir chaque liste tour à tour :
static ListeM plusProches2(ListeM[] reg, int x, int y){
  ListeM lpp = null;
  int dmin = -1, d;

  for(int i = 0; i < reg.length; i++){
    for(ListeM l = reg[i]; l != null; l = l.suiv){
      d = distance(x, y, l.xm, l.ym);
      if((dmin == -1) || (d < dmin)){
        dmin = d;
        lpp = new ListeM(null, l.xm, l.ym, l.num);
      }
      else
        if(d == dmin)
          lpp = new ListeM(lpp, l.xm, l.ym, l.num);
    }
  }
  return lpp;
}
c) La recherche de mobiles les plus proches prend le même nombre d'opérations dans les deux cas. Par contre, le coût de recherche est plus faible : il nécessite le calcul de h(num), puis une recherche dans une liste qu'on suppose de petite taille.
Question 4.
On parcourt les listes de reg en les coupant en deux à chaque fois :
static void divise(ListeM[] rega, ListeM[] regb, ListeM[] reg,
           int xa, int ya, int xb, int yb){
  /* on initialise rega et regb */
  for(int i = 0; i < reg.length; i++){
    rega[i] = null;
    regb[i] = null;
  }
  for(int i = 0; i < reg.length; i++)
    for(ListeM l = reg[i]; l != null; l = l.suiv)
      if(distance(xb,yb,l.xm,l.ym)<distance(xa,ya,l.xm,l.ym))
        regb[i] = new ListeM(regb[i], l.xm, l.ym, l.num);
      else
        rega[i] = new ListeM(rega[i], l.xm, l.ym, l.num);
}
Question 5.
Des étiquetages possibles sont :



Question 6.
a) Il suffit de montrer que j (b) £ D(G)+1 pour tout b. Or, pour chaque b, le nombre de valeurs différentes comprises dans l'ensemble des fréquences utilisées pour les voisines de b traitées précédemment est majoré par le nombre de voisines de b, donc inférieur à D(G).

b) Une clique contenant k balises a un degré égal à k-1 et toutes les balises ont k-1 voisins, ce qui implique qu'il faut exactement k fréquences pour l'étiqueter. Tout graphe contenant une telle clique a donc besoin d'au moins k fréquences et le résultat suit. Par suite n(G)³ w(G).

c) C'est le cas de la clique à n balises. Si D(G)=2 et G est un cycle d'ordre impair, alors n(G) = 3 :



Question 7.
a) Considérons par exemple le cas n=3 :



On montre par récurrence que le couple (bi, ci) est étiqueté avec la fréquence i pour tout i, ce qui conduit à N(j )=n.

b) Si n=1, le graphe est réduit à deux points non voisins, donc n(G1) = 1. Si n>1, la balise b0 a au moins un voisin, donc il faut au moins deux fréquences. En fait, deux suffisent : on attribue à tous les bi la fréquence 0 et aux ci la fréquence 1.
Question 8.
Pour chaque i, on remplit un tableau de booléens utilisee tel que utilisee[f] vaut vrai si la fréquence f est utilisée dans l'étiquetage des voisins j de i qui sont < i. Une fois cela fait, on cherche dans ce tableau la plus petite valeur égale à faux, qui nous donne la fréquence cherchée. Il est clair que n(G) £ n, donc un tableau de taille n est largement suffisant pour utilisee.
static void naif(Graphe G){
  boolean[] utilisee = new boolean[G.n];
  int j, k = 0;

  for(int i = 0; i < G.n; i++){
    /* on initialise */
    for(j = 0; j <= k; j++)
      utilisee[j] = false;
    /* on cherche toutes les fréquences utilisées */
    for(Liste l = G.vois[i]; l != null; l = l.suiv)
      if(l.b < i)
        utilisee[G.freq[l.b]] = true;
    /* on cherche la plus petite fréquence libre */
    for(j = 0; utilisee[j]; j++);
    G.freq[i] = j;
    if(j == k) k++;
  }
}
Question 9.
a) L'ordre a<c<b<d donne trois fréquences alors que deux suffisent :



b) Soit b une balise à qui l'algorithme naïf attribue la fréquence k. Cela veut dire qu'il existe k-1 intervalles Ibi = [xbi, xbi'] vérifiant xb1 £ xb2 £ ··· £ xbk-1. Par hypothèse xbk-1 £ xb et de plus [xb, xb'] a une intersection non vide avec les Ibi, ainsi xb est dans tous les Ibi et donc les bi et b forment une clique.

c) On construit le graphe associé aux rectangles [1, 10] × [1, 3], [3, 10] × [6, 8], [5, 13] × [4, 7], [6, 15] × [2, 5] de balises a, b, c, d :



Si on parcourt ce graphe dans l'ordre des xb, l'algorithme naïf lui attribue trois fréquences (a, 0), (b, 0), (c, 1) forçant (d, 2) alors que deux suffisent : (a, 0), (d, 1), (c, 0), (b, 1).
Question 10.
On doit chercher une composante connexe du graphe H(f1, f2) contenant b. Pour cela, on parcourt le graphe G en profondeur d'abord à partir de la balise b et on échange les fréquences :
/* Ici, on sait déjà que phi(b) = f1 ou f2 */
static void echangerAux(boolean[] visite, Graphe G, int b, int f1, int f2){
  if(! visite[b]){
    visite[b] = true;
    if(G.freq[b] == f1)
      G.freq[b] = f2;
    else
      G.freq[b] = f1;
    for(Liste l = G.vois[b]; l != null; l = l.suiv)
      if((G.freq[l.b] == f1) || (G.freq[l.b] == f2))
        echangerAux(visite, G, l.b, f1, f2);
  }
}

/* f1 = freq(b) */
static void echangerFreq(Graphe G, int b, int f1, int f2){
  boolean[] visite = new boolean[G.n];

  echangerAux(visite, G, b, f1, f2);
}
Question 11.
a) Nous allons montrer cette fois que pour tout i, (bi, ci) vont recevoir les fréquences (1, 0). Au départ, b0 reçoit 0 et c0 également. De même, b1 et c1 reçoivent 1. La balise b2 a deux voisines (c0, 0), (c1, 1). Or c0 n'est pas dans Hc1(1, 0) = {(b0, 0)}, donc on peut reétiqueter cette composante, ce qui attribue 0 à b0 et 1 à c1. Par récurrence, à partir de i = 3, on constate que toutes les balises cj (resp. bj) pour j<i voisines de bi (resp. ci) ont reçu la fréquence 0 (resp. 1) et donc bi reçoit la fréquence 1 (resp. 0).

b) On gère au plus juste la valeur de la plus grande fréquence utilisée, appelée ici k :
static void naifAmeliore(Graphe G){
  boolean[] utilisee = new boolean[G.n];
  int k = 0, j;

  for(int i = 0; i < G.n; i++){
    /* recherche de la plus petite fréquence non utilisée */
    for(j = 0; j <= k; j++)
      utilisee[j] = false;
    for(Liste l = G.vois[i]; l != null; l = l.suiv)
      if(l.b < i)
        utilisee[G.freq[l.b]] = true;
    /* on cherche la plus petite fréquence libre */
    for(j = 0; utilisee[j]; j++);
    if(j <= k)
      /* pas besoin d'une nouvelle fréquence */
      G.freq[i] = j;
    else{
      /* égalisons si c'est possible */
      boolean egalise = false;
      int f1 = -1, f2 = -1;

      for(Liste lb=G.vois[i]; (lb!=null) && !egalise; lb=lb.suiv)
        if(lb.b < i)
          for(Liste lc=lb.suiv; (lc!=null) && !egalise; lc=lc.suiv)
            if((lc.b < i) && (G.freq[lc.b] != G.freq[lb.b])){
              f1 = G.freq[lb.b];
              f2 = G.freq[lc.b];
              echangerFreq(G, lb.b, f1, f2);
              if(G.freq[lc.b] != f2)
                /* raté, lc.b était dans H[lb.b] */
                echangerFreq(G, lb.b, f2, f1);
              else
                egalise = true;
            }
      if(egalise)
        G.freq[i] = f1;
      else{
        k++;
        G.freq[i] = k;
      }
    }
  }
}
Pour chaque balise i, on doit examiner au plus d(i) (d(i)-1)/2 couples de balises voisines distinctes (b, c). Le calcul de Hb(j (b), j (c)) demande un parcours de G, donc au plus 2m opérations. Le coût est donc borné par O(n k)+O(n (D(G)2+m)) = O(n D(G)2).
Question 12.
Considérons le chemin [b, c], qui passe par les balises b0 = b, b1, b2, ..., br-1, br = c. On commence par enlever toutes les étiquettes de bi pour 0 < i £ r, puis on utilise l'algorithme naïf pour étiqueter b0, b1, ..., br dans cet ordre. La balise b0 se trouve avoir D-1 voisines de fréquences distinctes, il en reste une <D pour b0. Tant que i < r, la balise bi a au plus D-1 voisines étiquetées, donc on peut lui attribuer une fréquence <D. La balise br = c a strictement moins de D-1 voisines, donc il reste encore une fréquence <D pour c.
Question 13.
a) Soit c la première balise rencontrée sur le chemin de b1 à b2 ayant trois voisines de même fréquence f dans la composante F(b1, b2) :



On peut donc réétiqueter c avec une fréquence non utilisée f'' < D, ce qui scinde la composante connexe liant b1 à b2. On peut donc égaliser (b1, f1) et (c, f'') ce qui permet de libérer f1 pour la donner ultérieurement à b.

b) Appelons y la balise commune. Elle a quatre voisines étiquetées avec deux fréquences seulement. On peut donc trouver une autre fréquence <D non encore utilisée f'' pour y, ce qui permet de séparer encore une fois b1 et b2 et on peut égaliser (b1, f1) et (y, f''), libérant f1 pour b.



Question 14.
Remarquons tout d'abord qu'on peut supposer qu'il n'existe qu'un chemin de b1 à b2 dans F(b1, b2) (de même pour les autres balises). En effet, s'il existait deux chemins, b1 aurait deux voisines de même fréquence f2, donc on pourrait appliquer la question 12.

Soit y le premier sommet rencontré dans le chemin allant de b1 à b2 dans F(b1, b2). Il est nécessairement de fréquence f2 et différent de b2, car b1 et b2 ne sont pas voisines. On égalise (b1, f1) et (b3, f3). Deux cas peuvent se produire : si l'égalisation ne crée pas de chemin de (b1, f3) vers (b2, f2) dans la composante F'(b1, b2) du nouveau graphe, on égalise (b1, f3) et (y, f2) et on a réussi à égaliser b1 et b2. Si l'égalisation a créé un nouveau chemin de b1 vers b2, les deux composantes F'(b1, b2) et F'(b1, b3) ont un sommet commun et on peut appliquer la question précédente.

Donnons un exemple illustrant le premier cas :



Les deux balises b1 et b2 ne sont pas adjacentes. On égalise b1 et b3, ce qui donne :



puis on égalise b1 et y :



ce qui permet d'attribuer la fréquence f2 = 2 à la balise b.

Pour le second cas, considérons :



L'égalisation de b1 et b3 crée un nouveau chemin entre b1 et b2 :



Question 15.
Raisonnons par récurrence sur le couple (D, n) avec D³ 3. On suppose la propriété vraie pour tout graphe G de degré D(G) £ D et n' < n.

Soit donc G de degré D et nombre de sommets n. D'après la question 12, il suffit de considérer le cas où G est D-régulier. De même, on peut supposer G connexe, quitte à raisonner sur chacune de ses composantes connexes. Soit b une balise de G. On considère le graphe G' obtenu en enlevant b.

Supposons d'abord D(G') < D(G). Si 3 £ D(G') < D(G), l'hypothèse de récurrence nous dit que l'on peut étiqueter G' avec au plus D(G') £ D(G)-1 fréquences : il restera donc une fréquence de libre pour l'attribuer à b et on étiquette G avec au plus D(G)-1+1 fréquences, ce qui prouve le résultat.

Si D(G')=2, on sait que l'on peut étiqueter G' avec deux fréquences si G' ne contient pas de cycle de longueur impaire et trois sinon. Dans les deux cas, on a étiqueté G' avec au plus D(G) fréquences.

Supposons maintenant D(G') = D(G) ³ 3. Le graphe G' n'est pas une (D+1)-clique, car les voisines de b sont de degré <D dans G'. On peut étiqueter G' avec au plus D(G') = D(G) fréquences par hypothèse de récurrence.

Soient b1, b2, ..., bD les balises voisines de b dans G. Soit c l'une des balises de G - {b, b1, b2, ..., bD }. Si les voisines de c sont étiquetées avec moins de D-1 fréquences, on applique la question 12.

On choisit alors deux balises bi et bj non adjacentes (ce qui est possible car G n'est pas une D+1-clique). S'il existe une balise bk telle que F(bi, bk) et H(bj, bk) aient un point commun, on applique la question 13b. Sinon, on applique 14.
This document was translated from LATEX by HEVEA.