
1 Вариант
type
st = (left, middle, right);
nat = 1..maxint;
var
m: nat; {m – число дисков}
procedure move(n: nat; s1, sw, sk: st);
{перемещение n дисков с s1 на sk}
procedure step;
{перемещение одного диска с s1 на sk}
procedure print(s: st);
begin
case s of
left: write(' лев. ');
middle: write(' средн. ');
right: write(' прав. ')
end;
end;
begin {step}
write(' снять диск с ');
print(s1);
write(' надеть на ');
print(sk);
writeln
end;
begin {move}
if n = 1 then
step
else begin
move(n - 1, s1, sk, sw);
step;
move(n-1, sw, s1, sk)
end
end;
begin
writeln('введите число дисков')
read(m); {число дисков}
writeln('для ', m:3, ' дисков следует произвести ',
'следующие действия:');
move(m, left, middle, right);
end.
2 Вариант
Procedure Towers(n:byte; A, C, B : char)
{переложить N колец с А на С, используя В как промежуточный}
begin
if n=1 then writeln(A, ‘ ® ’, C)
else begin
Towers(n-1, A, B, C);
writeln(A, ‘ ® ‘, C);
Towers(n-1, B, C, A)
end
end;
begin
Towers(4, ‘A’, ‘C’, ‘B’) { решаем задачу при N = 4 }
end.
Пример 13. Задача о "восьми ферзях" — хорошо известный пример использования метода проб и ошибок и алгоритмов с возвратом. Этой задачей в 1780 г. занимался К. Ф. Гаусс, но полного ее решения не дал, и это не удивительно. Для подобного класса задач характерно отсутствие аналитического решения, но они требуют большого объема изнурительных вычислений, терпения, аккуратности. Поэтому такие задачи отдают для решения вычислительным машинам, обладающим вышеперечисленными достоинствами в полной мере.
Задача о восьми ферзях формулируется так: нужно расставить восемь ферзей на шахматной доске размером 8×8 таким образом, чтобы ни один ферзь не угрожал другому, то есть чтобы ни один из них не находился на одной и той же горизонтали, вертикали или диагонали, что и любой другой.
Грубый вариант решения задачи:
PROCEDURE Try(i: INTEGER);
BEGIN
инициация выбора положения i-гo ферзя;
REPEAT выбор очередного положения;
IF безопасно THEN поставить ферзя;
IF i < 8 THEN Try(i+1);
IF неудача THEN убрать ферзя END
END
END
UNTIL удача OR мест больше нет
END Try;
Чтобы идти дальше, нужно остановиться на каком-либо представлении для данных. Поскольку из шахматных правил мы знаем, что ферзь бьет все фигуры, находящиеся на той же самой вертикали, горизонтали или диагонали, то заключаем, что на каждой вертикали может находиться один и только один ферзь, поэтому при поиске места для i-го ферзя можно ограничить себя лишь i-й вертикалью. Таким образом, параметр i становится индексом вертикали, а процесс выбора возможного местоположения ограничивается восемью допустимыми значениями для индекса горизонтали j.
Остается решить вопрос: как представлять на доске эти восемь ферзей? Очевидно, доску вновь можно было бы представить в виде квадратной матрицы, но после небольших размышлений мы обнаруживаем, что это значительно усложнило бы проверку безопасности поля. Конечно, подобное решение нежелательно, поскольку такая операция выполняется очень часто. Поэтому хотелось бы остановиться на таком представлении данных, которое, насколько это возможно, упростило бы проверку. В этой ситуации лучше всего делать непосредственно доступной именно ту информацию, которая действительно важна и чаще всего используется. В нашем случае это не поля, занятые ферзями, а сведения о том, находится ли уже ферзь на данной горизонтали или диагонали. (Мы уже знаем, что на каждой k-й вертикали (1 <= k <= i) стоит только один ферзь.) Эти соображения приводят к таким описаниям переменных:
VAR x: ARRAY [1..8] OF INTEGER;
a: ARRAY [1..8] OF BOOLEAN;
b: ARRAY [b1..b2] OF BOOLEAN;
c: ARRAY [c1..c2] OF BOOLEAN;
где
xi обозначает местоположение ферзя на i-й вертикали;
aj указывает, что на j-й горизонтали ферзя нет;
bk указывает, что на k-й /-диагонали ферзя нет;
сk указывает, что на k-й \-диагонали ферзя нет.
Выбор границ индексов b1, b2, с1, с2 определяется исходя из способа вычисления индексов для b и с. На /-диагонали у всех полей постоянна сумма координат i и j, а на \-диагонали постоянна их разность. Оператор поставить ферзя превращается в такие операторы:
х[i] := j; a[j] := FALSE; b[i+j] := FALSE; c[i-j] := FALSE;
а оператор убрать ферзя — в такие:
a[j] := TRUE; b[i+j] := TRUE; c[i-j] := TRUE;
Условие безопасно выполняется, если поле с координатами i, j лежит на горизонтали и вертикали, которые еще не заняты. Следовательно, ему соответствует логическое выражение
а[j] & b[i+j] & c[i-j];
program ferz;
uses crt;
var s,i:integer;
a:array[1..8] of boolean;
b:array[2..16] of boolean;
c:array[-7..7] of boolean;
x:array[1..8]of integer;
procedure print;
var k:integer;
begin
s:=s+1;
write('Решение номер ',s:2,' : ');
for k:=1 to 8 do write(x[k]:4);
writeln;
write('Press <Enter>');
readln;
end;{of print}
procedure tr (i:integer);
var j:integer;
begin
for j:=1 to 8 do
if a[j]and b[i+j]and c[i-j] then
begin
x[i]:=j;
a[j]:=false;b[i+j]:=false;c[i-j]:=false;
if i<8 then tr(i+1) else print;
a[j]:=true;b[i+j]:=true;c[i-j]:=true;
end;
end;{of try}
begin{of main}
clrscr;
for i:=1 to 8 do a[i]:=true;
for i:=2 to 16 do b[i]:=true;
for i:=-7 to 7 do c[i]:=true;
s:=0;
tr(1)
end.