for I := 0 to N-1 do

  c[i] := 0;

  while c[0] = 0 do

  begin

  { Вычисление суммы при текущей комбинации знаков }

  p := 1;

  for I := 2 to N do

  case c[i-1] of

  0: Inc(p, i);

  1: Dec(p, i);

  2: p := p * I;

  3: p := p div I;

  end;

  if p = S then OutputSigns;

  { Следующая комбинация }

  Inc(c[N-1]);

  I := N-1;

  while c[i] = 4 do

  begin

  c[i] := 0;

  Inc(c[i-1]);

  Dec(i);

  end;

  end; { while }

end.

Задача 9 « О скобках»

Поострить все слова длины n>0 в алфавите скобок «(» и «)», представляющие правильные скобочные записи.

Алгоритм состоит в следующем:

  Будем рекурсивно строить слова из скобок. Также  для соблюдения верности расстановки будем хранить  код строки Code: при новой скобке '(' Code увеличивается на 1, а при новой скобке ')' Code уменьшается на 1 (в конце должно выполняться Code = 0). На каждом шаге должно выполняться  Code > 0. По сути, на каждом шаге значение Code  есть разность количества открывающих и закрывающих  скобок.

  Пусть на некотором шаге имеем слово S. Пробуем добавить скобки:

  '(' - возможно, если до конца полного слова (длины N), возможно будет закрыть все открывающие скобки, т. е. если N - Length(S) > Code;  ')' - возможно, если впереди есть хотя бы одна незакрытая  открывающая  скобка,  т. е.  если  Code > 0.  При достижении длины слова, равной N, выводим слово.

НЕ нашли? Не то? Что вы ищете?

program Problem8;

uses  Crt;

const

  N = 10; { N должно быть четным! }

Var  S: String;  Code: Integer;

procedure BuildWords;

begin

  if Length(S) = N then

  begin

  if Code = 0 then Writeln(S)

  end

  else  begin

  { '(' }

  if N - Length(S) > Code then

  begin

  S := S + '(';

  Inc(Code);

  BuildWords;

  { Откат }

  Dec(Code);

  S[0] := Pred(S[0]); { уменьшение длины слова на 1 }

  end;

  { ')' }

  if Code > 0 then

  begin

  S := S + ')';

  Dec(Code);

  BuildWords;

  { Откат }

  Inc(Code);

  S[0] := Pred(S[0]); { уменьшение длины слова на 1 }

  end;

  end;

end;

Задача 10

Дана строка S и набор слов A[1], …, A[n]. Разбить строку S на слова набора всеми возможными способами.

Решение:

Общий алгоритм состоит в следующем. Рекурсивно пробуем разбить последовательно (слева направо) строку S на слова и проверить, чтобы они содержались в наборе и, к тому же, не повторялись.

Решение оформлено в виде рекурсивной процедуры Solve(pos: Integer), которая производит разбиение части строки S, начиная с символа pos+1. Принцип ее работы таков. Пробуем выделять слова S[(pos+1)..(pos+1)], S[(pos+1)..(pos+2)], …, S[(pos+1)..Length(S)]. Каждое такое слово ищем в наборе и проверяем, не использовано ли оно на текущий момент в текущем частичном разбиении. Если все верно, добавляем слово (а, точнее, его индекс) в разбиение и запускаем подпроцедуру.

В задаче приходится много раз искать слова в наборе. Это, явно, гораздо быстрее делать двоичным поиском (за время O(log2N)). Для этого требуется предварительная сортировка набора А[ ]. Но поскольку при сортировке приходится обменивать значения элементов, а обмен строк (по сути, обмен массивов) – дело довольно долгое, вместо этого удобнее сортировать указатели на слова:

A[i] – i-ое слово до сортировки,

p[i] – указатель на i-ое слово после сортировки, фактически значение i-ого слова после сортировки будет A[p[i]].

Например, если вначале A = {‘Z’, ‘A’,  ‘CC’}, то после сортировки массив А не изменится но массив p будет выглядеть так: p = {2, 3, 1} (т. к. отсортированный массив должен был быть таким: A = {‘A’, ‘CC’, ‘Z’}).

После сортировки возможен двоичный поиск в наборе. Он оформлен в виде функции FindWord(W: String): Integer, где W – искомое слово. Функция возвращает либо номер искомого слова (т. е. W=A[p[FindWord(W)]]), либо 0, если такого слова нет. Логический массив used[ ] …

program Problem10;

uses

  Crt;

const

  MaxN = 100;

  MaxWordLength = 10;

type

  ShortStr = String[MaxWordLength];

var

  N, i: Integer;

  S: String;

  A: array [1..MaxN] of ShortStr;

  p: array [1..MaxN] of Integer;

  used: array [0..MaxN] of Boolean;

  razb: array [0..MaxN] of Integer;

{ Двоичный поиск заданного слова в

  отсортированном наборе }

function FindWord(W: String): Integer;

var

  L, R, C: Integer;

begin

  L := 1;

  R := N;

  while R - L > 1 do

  begin

  C := (L + R) div 2;

  if A[p[C]] > W then

  R := C

  else

  L := C;

  end;

  FindWord := 0;

  if A[p[L]] = W then

  FindWord := L

  else

  if A[p[R]] = W then

  FindWord := R;

end;

{ Сортировка указателей на слова набора }

procedure SortWords;

var

  i, j, tmp: Integer;

begin

  { Пузырек ;) }

  for j := N-1 downto 2 do

  for i := 1 to j do

  if A[p[i]] > A[p[i+1]] then

  begin

  tmp := p[i];

  p[i] := p[i+1];

  p[i+1] := tmp;

  end;

end;

{ Рекурсивная процедура-решение задачи }

{ pos - позиция последнего занятого символа в S }

procedure Solve(pos: Integer);

var

  i: Integer;

begin

  if pos = Length(S) then

  begin

  for i := 1 to razb[0] do

  Write(A[p[razb[i]]], ' ');

  Writeln;

  End

  else

  begin

  for i := pos + 1 to Length(S) do

  begin

  { Можно ли добавить выделенное слово? }

  if not used[FindWord(Copy(S, pos+1, i - pos))]  then

  begin

  Inc(razb[0]);

  razb[razb[0]] := FindWord(Copy(S, pos+1, i - pos));

  used[razb[razb[0]]] := True;

  Solve(i);

  { Откат }

  used[razb[razb[0]]] := False;

  Dec(razb[0]);

  end;

  end;

  end;

end;

begin

  ClrScr;

  Readln(S);

  Readln(N);

  for i := 1 to N do

  begin

  Readln(A[i]);

  p[i] := i;

  used[i] := False;

  end;

  ClrScr;

  SortWords;

  razb[0] := 0;

  used[0] := True;

  Solve(0);

end.

Задача 11

Задана матрица из натуральных чисел А[1..n, 1..m], n<=m. За каждый проход через клетку (i, j) взимается штраф A[i, j]. Необходимо определить минимальный суммарный штраф, с которым можно пройти из клетки (1,1) в клетку (n, m). При этом из текущей клетки можно переходить в любую из 3 соседних клеток, находящихся в строке с номером, на 1 большим, чем текущий номер строки.

Program problem 11;

Var

N, M, i, j: byte;

A:array [1..100, 1..100] of byte;

MinP: array [1..100, 1..100] of word;

Procedure Solution (i, j: byte);

begin

  if i=N then Exit;

  {Where can we go from (i, j) ?}

  {Down and left}

  if (j>1) and (MinP[i+1, j-1] > MinP [i,  j] + A[i,  j]) then

  begin

  MinP[i+1, j-1]:= MinP[i, j] + A[i, j];

  Solution(i+1, j-1);

  end;

  {Down}

  if MinP[i+1, j] > MinP[i, j] + A[i, j] then

  begin

  MinP[i+1, j]:= MinP[i, j] + A[i, j] ;

  Solution(i+j, j);

  end;

  {Down and right}

  if (j < M) and (MinP[i+1, j+1] > MinP[i, j] + A[i,  j] then

  begin

  MinP[i+1, j+1]:= MinP[i, j] + A[i, j] ;

  Solution(i+j, j+1);

  end;

  end;

Begin

  {input data}

Writeln (‘ input N &  M …’);

Readln (N, M);

Writeln (‘input matrica A[‘, N,  ‘,  ‘,  M,  ‘];

For i:=1 to N do

For j:=1 to M do

Read (A[i, j]);

{Initializing MinP}

FillChar (MinP, SizeOf(MinP), $FF);

{Solution}

MinP[1,1]:=0;

Solution(1, 1);

{Output results}

if MinP[N, M]=$FFFF then

writeln (‘There isn’t path from (1, 1) to (‘,  N, ‘ ,  ‘, M,  ‘) .’)

  else

writeln (MinP[N, M] + A[N, M]);

End.

Задача 12"Суммы"
Любое целое число N>0 можно представить в виде суммы натуральных слагаемых, каждое из которых является степенью числа 2. Суммы, различающиеся лишь порядком слагаемых, считаются одинаковыми.

Найти K - число способов, которыми можно представить число N в виде суммы натуральных слагаемых, каждое из которых является степенью числа 2.

Например, для N=7 :

7=4+2+1

7=4+1+1+1

7=2+2+2+1

7=2+2+1+1+1

7=2+1+1+1+1+1

7=1+1+1+1+1+1+1

и, следовательно, K=6

Составим подряд несколько разложений:

N=1 N=2  N=3  N=4  N=5  N=6  N=7

1  2  2+1  4  4+1  4+2  4+2+1

  1+1  1+1+1  2+2  2+2+1  4+1+1  4+1+1+1

  2+1+1  2+1+1+1  2+2+2  2+2+2+1

  1+1+1+1  1+1+1+1+1  2+2+1+1  2+2+1+1+1

  2+1+1+1+1  2+1+1+1+1+1

  1+1+1+1+1+1 1+1+1+1+1+1+1

Введем обозначение

K(i)- количество разложений числа i по условиям задачи

Сразу замечаем, что

  K(1)=1, K(2)=2

  K(i)=K(i-1), если i - нечетное

  (в каждой из строк для соответствующего

  предыдущего разложения добавляется +1,

  а количество строк не меняется)

K(i) зависит от K(i-1) следующим образом K(i)=K(i-1) + X

То есть количество разложений i-того числа, равно количеству разложений i-1-го числа плюс еще сколько то. Сколько же?

Для примера рассмотрим детальнее разложения при N=6 и отметим те строки, которые перешли в разложение N=6 из разложения N=5 добавлением +1:

  N=5  N=6

Из за большого объема этот материал размещен на нескольких страницах:
1 2 3 4 5