Quatre musiciens sont à gauche d'un pont; il fait nuit et il y a une seule torche; deux personnes au maximum peuvent traverser à la fois, et celui ou ceux qui traversent doivent avoir la torche pour éclairer le pont, sinon ils tombent dans l'eau. Le premier met 10 minutes pour traverser le pont, le deuxième 5 minutes, le troisième 2 minutes et le quatrième une minute.
Si le premier traverse en même temps que le deuxième, le temps de leur traversée sera de 10 minutes.
Ils doivent traverser le pont en 17 minutes.
Introduction
Nous allons réaliser le programme qui trouve les solutions de ce problème.
On commence par présenter les données :
1) - les musiciens qui sont à gauche du pont
2) - les musiciens qui sont à droite du pont
3) - le temps de traversée pour chacun
4) - le temps total de traversée
5) - l'endroit où se trouve la torche.
Et voici la modélisation de ces données :
1)
- gauche : array[1..4] of boolean;
Si le premier musicien est à gauche du pont alors gauche[1]=true, sinon false. En même temps que gauche[1]=true, on a droite[1]=false, car le musicien ne peut pas se trouver en deux endroits différents au même moment
2)
- droite : array[1..4] of boolean;
Si le premier musicien est à droite du pont alors droite[1]=true, sinon
false.
3) const temps : array[1..4] of integer = (10,5,2,1); // minutes nécessaires
4) var minutes : integer;
5) var torche_a_gauche : boolean;
Analyse du problème
Les musiciens traversent le pont au maximum deux à la fois.
S'ils sont deux, les possibilités sont :
1,2; 1,3; 1,4; 2,3; 2,4; 3,4
Cela se traduit par deux boucles imbriquées :
for i:=1 to 4 do
for j:=i+1 to 4 do
Si à un instant donné on a la torche à gauche et qu'on veut traverser
le pont, alors on considère que la torche va passer à droite, donc on écrit
tout simplement :
torche_a_gauche:=false;
La traversée d'un musicien de gauche à droite se traduit par :
gauche[i]:=false;
droite[i]:=true; //--- "i" étant le numéro du
musicien
Ce programme étant récursif (c'est à dire qu'il fait des essais de
traversée successifs de la gauche vers la droite, de la droite vers la gauche
et ainsi de suite), il faut mémoriser la position en cours avant chaque
traversée (de façon à la restaurer si la traversée faite débouche plus tard sur
un échec (temps total supérieur à 17 minutes)).
On note le sens de traversée par un entier égal à "1" ou
"-1", ce qui facilite l'appel récursif avec un appel
"chercher(-sens)"; au début sens=1, ensuite dans le parcours de
l'arbre, suivant le niveau de profondeur, il sera égal à 1 ou -1.
On va ajouter un autre paramètre à la procédure "chercher"
qui représente le chemin parcouru à un instant donné, au bout de n traversées.
Si, par exemple, on a deux musiciens ("i" et "j")
qui traversent le pont, alors on les mets dans une chaîne de caractères
"s1" :
s1:=inttostr(i)+','+inttostr(j);
On appelle ensuite récursivement la procédure "chercher",
avec le chemin déjà parcouru noté "s" auquel on ajoute le sens de la
traversée (indiqué par une flèche "<-" ou "->";
chercher(-sens,s+'
<-'+s1+';');
On doit ensuite prendre en compte le dernier déplacement effectué par
un ou deux musiciens. En effet, rien n'empêche le programme récursif de faire
le mouvement suivant:
- déplacement vers la droite du musicien 1. La torche se trouve donc à
droite.
- déplacement vers la gauche du musicien 1. La torche est maintenant à
gauche.
- déplacement vers la droite du musicien 1. La torche est maintenant à
droite.
Et ainsi de suite, ce qui nous entraîne dans une boucle infinie (en fait
ici elle n'est pas infinie à cause du temps qui s'additionne pendant les
traversées; mais cela nous fait perdre énormément de temps pendant la
récursivité). On doit donc sauvegarder le dernier mouvement effectué et le
comparer à celui que nous allons effectuer. Supposons que nous déplaçons le
musicien "1" vers la droite; on mémorise dans une chaîne
"1". Si ensuite on veut déplacer "1" vers la gauche, on
compare avec la chaîne mémorisée, et on voit qu'elles sont égales, donc ce
mouvement ne sera pas effectué.
Voici l'entête de la procédure "chercher" :
procedure chercher(sens : integer;
s,last : string);
où "s" est le chemin parcouru et "last" le(s)
musicien(s) ayant effectué la dernière traversée; pour deux musiciens on a :
s1:=inttostr(i)+','+inttostr(j);
if
s1<>last then
chercher(-sens,s+' <-'+s1+';',s1);
Une traversée complète d'un musicien "i" sera donc codée par
:
droite[i] := true; gauche[i] := false; // traversée
torche_a_gauche := false; // on déplace la torche à droite
inc(minutes, temps[i]); // addition nombre de minutes
s1 := inttostr(i);
if s1 <> last then // si mouvement différent du mouvement précédent
chercher(-sens, s + s1 + '->;', s1); // alors appel récursif
droite[i] := false; gauche[i] := true; // restauration positions initiales
torche_a_gauche := true; // on remet la torche à gauche
dec(minutes, temps[i]); // restauration nombre de minutes
La procédure récursive sera donc constituée de deux boucles imbriquées,
et de nombreux tests pour déterminer quels sont les musiciens qui se déplacent,
d'affectations de positions et de restauration de positions. Il est plus
intéressant de commencer par déplacer les musiciens deux par deux au lieu de un
par un (c'est à dire tout seuls).
Le point d'arrêt de la procédure est le suivant :
if minutes = 17 then
if droite[1] and droite[2] and droite[3] and droite[4] then
begin
fini := true;
listbox1.items.add('trouvé !');
listbox1.items.add(s);
listbox1.refresh;
exit;
end
else
exit;
En exécutant cette procédure on constate que le programme affiche des
solutions doubles. Cela est dû à l'opération suivante :
une fois qu'on a fait "3,4 ->" alors on teste :
"<- 1,2"
"<- 1,3" 3 part tout seul à gauche car
droite[1]=false; (*)
"<- 1,4" 4 part tout seul à gauche car
droite[1]=false;
"<- 2,3" 3 part tout seul à gauche car
droite[1]=false; (*)
L'ordinateur prend donc deux fois le musicien "3" (pendant le
parcours récursif) et le déplace à gauche, et comme ce mouvement mène à la
solution, on a des doublons dans l'affichage.
Il faut donc mémoriser les solutions trouvées et comparer chaque
nouvelle solution à celles déjà trouvées.
Télécharger
le code source Delphi