Untitled Document

 

 

Maths - Physique - Informatique / Collège, Lycée et +

 

Untitled Document

 

Commander

 

 

KöMaL - C'est quoi ?

 

 

Rédaction

 

 

 

Solutions des exercices d'informatique

octobre 2004.

Les solutions publiées ici ne sont pas détaillées et dans certains cas, nous ne donnons que le résultat. Pour l'obtention du nombre de points maximal, il est nécessaire de donner plus de précisions. Les abonnés ont accès aux solutions détaillées ainsi qu'à des articles supplémentaires.

 

S

S. 2. Nous avons trouvé une grotte à trésor qui contient n trésors de masses et de valeurs différentes. De ces trésors, nous aimerions mettre quelques-uns dans notre sac à dos de charge maximale w kg, de façon à avoir un trésor de valeur maximale.

Ecrire un programme calculant la valeur maximale qu'on peut emporter. La première ligne de l'entrée du programme contiendra n, la deuxième ligne w. Les n lignes suivantes contiennent la masse et la valeur d'un des n trésors. ( ces données sont considérées comme correctes, on ne demande pas le contrôle ) La sortie du programme sera un seul nombre, la valeur maximale à emporter.

Exemple :

Input

Output

6

1030

10

 

3 300

 

2 175

 

5 540

 

3 310

 

11 15000

 

4 420

 

 

A renvoyer : Le programme, sa brève description, le résultat d'exécution sur les données de test. Ne renvoyer que les programmes qui ont des résultats corrects sur les tests donnés avec résultat ci-dessous.

Correction : Vous pouvez obtenir des points si votre programme correspond à l'énoncé, il peut être compilé, il est bien commenté et il donne le bon résultat sur les données des petits tests. (max. 5 points). Vous pouvez obtenir 3 points de plus si le programme produit les bons résultats sur les données des grands tests (10 minutes de temps d'exécution max. ). Si le programme donne les bons résultats sur les données des tests géants (en 1 heure max.), on donne 1 point extra pour chacun de ces tests.

Données de tests

Taille

Données

Résultat correcte

Minus

testminus

1030

1. Petit

testpetit_1

7763

2. Petit

testpetit_2

 

3. Petit

testpetit_3

 

1. Grand

test_g_1

2502635

2. Grand

test_g_2

26272

3. Grand

test_g_3

 

4. Grand

test_g_4

 

5. Grand

test_g_5

 

6. Grand

test_g_6

 

1. Géant

test_geant_1

 

2. Géant

test_geant_2

 

 

(10 points)

 


Az S

La solution de l’exercice S. 2.

Pour une solution simple, il est conseillé d’utiliser la programmation dynamique. De quoi s’agit-il ? Nous allons d’abord présenter cette méthode à travers un exemple concret..

Exercice : Calculer le n ème nombre de Fibonacci.

Première solution :

#include <stdio.h>
#include <sys/types.h>
 
int n;
 
long int fib(int n1)
{
  if (n1 < 2) return(n1);
  return(fib(n1-1)+fib(n1-2));
}
 
main()
{
  scanf("%d",&n);
  printf("F(%d)=%d\n",n,fib(n));
}

C’est le programme récursif correspondant à la définition des nombres de Fibonacci.  En le testant, nous avons des surprises déjà dans le cas de n=45 . Si l’on calcule le nombre d’instruction nécessaires, on obtient  au moins F(n) pas pour calculer F(n) , ce qui est supérieur à an-2 (où a~=1.618), donc un grand nombre exponentiel. Déjà pour calculer F(45, le programme met  42 secondes et exécute plus de F(45)=1134903170  instructions. D’où vient cette lenteur ? En examinant le déroulement du programme, on se rend compte qu’il calcule certaines données de nombreuses fois (par exemple la valeur de F(2) ).

La méthode qui permet d’éviter ce surplus de travail s’appelle la programmation dynamique. L’idée est que si l’on a déjà calculé une donnée, on la note et au lieu de recalculer chaque fois que l’on en a besoin, on la cherche dans nos notes. Les résultats partiels doivent être calculés dans le bon ordre, du plus petit au plus grand pour que les données nécessaires à un calcul soient déjà disponibles.

Qu’est-ce que cela signifie pour l’exemple ci-dessus ? Que nous avons intérêt de calculer pour chaqu i la valeur de F(i) , comme par exemple dans le programme ci-dessous :

#include <stdio.h>
#include <sys/types.h>
 
long int F[50], n, i;
 
main()
{
  scanf("%d",&n);
  F[0]=0;
  F[1]=1;
  for (i=2; i<=n; i++) F[i]=F[i-1]+F[i-2];
  printf("F(%d)=%d\n",n,F[n]);
}

Ce programme met 0,01 seconde pour calculer la valeur de F(45), et exécute pour cela n+1 instructions.

La solution de l’exercice du sac à dos avec la méthode de programmation dynamique

Nous devons d’abord définir les calculs partiels. Nous calculons pour chaque 0≤i<n-re et chaque j≤w la valeur maximale pouvant être chargée, en utilisant les i+1 premiers objets, dans le sac à dos de capacité  j. (les objets sont numérotés de 0 à  n-1.) Ceci est chargé dans une pile T , donc pour une valeur de i donné, T [  j ] désigne la valeur pouvant être chargée, en utilisant les i+1 premiers objets, dans le sac à dos de capacité  j. Mais si l’on utilise la même pile T pour chaque i,   on doit faire attention à calculer les nouvelles valeurs T[j] dans le bon ordre.

Nous pouvons remarquer que pour calculer une nouvelle valeur T[j] , deux cas se présentent :

  • En utilisant les premiers i+1 objets, on peut charger dans le sac de capacité j la même valeur qu’en utilisant les i premiers objets, alors la valeur de T[j] ne change pas, ou bien
  • Nous pouvons charger un valeur plus grande et donc on doit utiliser le i ème objet , et ainsi T[j]=E[i]+T[j-V[i]], si T[j-V[i]] contient la valeur maximale pouvant être chargée en utilisant les i premiers objets. (Ici E[i] désigne la valeur du i ème objet, V[i] sa masse. On utilise le principe de la suboptimalité qui dit en grandes lignes que dans une solution optimale une solution partielle donne nécessairement la solution optimale du problème partiel correspondant. Si ceci n’est pas valable dans un certain cas, alors nous n’avons aucune chance de trouver avec la programmation dynamique la solution optimale.)

Le code correspondant :

for (i=0; i<=w; i++) T[i] = 0;
for (i=0; i<n; i++) {
  for (j=w; j>=V[i]; j--) {
    if (T[j-V[i]]+E[i] > T[j]) {
      T[j] = T[j-V[i]]+E[i];
    }
  }
}
printf("Valeur maximale : %d\n", T[w]);

Ceux qui ont donné cette méthode, ont déjà obtenu  9 points. Ce programme fonctionne bien seulement si la valeur de w est petite. Si w est grand mais la valeur possible des trésors est petite, on peut alors écrire un autre programme dynamique seblable. totvaleur désigne la somme des valeurs des objets (si on connaît une meilleure estimation pour majorer la valeur optimale, on peut aussi l’écrire dans cette variable). Dans la  i ème itération T[j] contient la masse minimale pour charger exactement la valeur j en utilisant les premiers  i+1 objets. Si on ne peut pas le faire du tout, alors on charge la valeur w+1 ( au lieu de l’infini ). Ici, il est vrai aussi que, en calculant T[j] , si la solution optimale ne contient pas le i ème objet, alors la valeur T[j] ne change pas ; si elle le contient, alors T[j]=T[j-E[i]]+V[i]. Le code correspondant :

for (i=1; i<=totvaleur; i++) T[i] = w+1;
T[0] = 0;
for (i=0; i<n; i++) {
  for (j=totvaleur; j>=E[i]; j--) {
    if ((T[j-E[i]]+V[i] < T[j])) {
      T[j] = T[j-E[i]]+V[i];
    }
  }
}
for (i=totvaleur; i>=0; i--) {
  if (T[i] <= w) {
    max = i;
    i = 0;
  }
}
printf("Valeur maximale : %d\n", max);

Parmi les deux solutions il faut choisir celle qui serait plus rapide dans un cas précis. Vous pouvez télécharger le programme complet en C  ici .

 

#include <stdio.h>
 
main()
{
  int *E, *V, *T;
  int n, w, v1, e1, i, j, max;
  int akt_n = 0;
  int totvaleur = 0;
 
  scanf("%d%d",&n,&w);
 
  if ((E = (int *) malloc(n*sizeof(int))) == NULL) {
    printf("Allocation error\n");
    exit(2);
  }
  if ((V = (int *) malloc(n*sizeof(int))) == NULL) {
    printf("Allocation error\n");
    exit(2);
  }
 
  for (i=0; i<n; i++) { 
   scanf("%d%d",&v1,&e1);
    if (v1 <= w) {
               V[akt_n] = v1;
               E[akt_n] = e1;
               totvaleur += e1;
               akt_n++;
    }
  }
 
      n = akt_n;
 
 if (w <= totvaleur) {
  if ((T = (int *) malloc((w+1)*sizeof(int))) == NULL) {
    printf("Allocation error\n");
    exit(2);
  }
  for (i=0; i<=w; i++) T[i] = 0;
  for (i=0; i<n; i++) {
      for (j=w; j>=V[i]; j--) {
                 if (T[j-V[i]]+E[i] > T[j]) {
                     T[j] = T[j-V[i]]+E[i];
                 }
      }
  }
  printf("Valeur maximale : %d\n", T[w]);
 
 } else {
  if ((T = (int *) malloc((totvaleur+1)*sizeof(int))) == NULL) {
    printf("Allocation error\n");
    exit(2);
  }
  for (i=1; i<=totvaleur; i++) T[i] = w+1;
  T[0] = 0;
  for (i=0; i<n; i++) {
      for (j=totvaleur; j>=E[i]; j--) {
                 if ((T[j-E[i]]+V[i] < T[j])) {
                     T[j] = T[j-E[i]]+V[i];
                 }
      }
  }
  for (i=totvaleur; i>=0; i--) {
    if (T[i] <= w) {
      max = i;
      i = 0;
    }
  }
 printf("Valeur maximale : %d\n", max);  }  }

Untitled Document

©opyright Acclim'PCI 2004-2010

Nos Partenaires :

 

Journal de Maths-Physique KöMal

 

Société de Mathématiques Jànos Bolyai

 

Société de Physique Lorànd Eötvös