printРешение

Тема: динамическое программирование
Сложность: средняя

Решить эту задачу можно несколькими способами. Первый способ – динамическое программирование. Храним в таблице kol минимальное число слагаемых для получения числа `i`, а таблице how – какое слагаемое было использовано для получения числа `i` оптимальным образом. Первоначально все элементы массива kol содержат какое-то большое число, например, 1000. Массив gnome содержит гномьи слагаемые.
var gnome:array[1..54] of integer;
    kol,how:array[0..1000000] of integer;
...
for i:=0 to 1000000 do
  kol[i]:=1000;
Тогда заполнить массивы kol и how можно следующим образом:
kol[0]:=0;
for i:=0 to 1000000 do
  for j:=1 to 54 do
  begin
    k:=gnome[j]+i;
    if k<=1000000 then
      if kol[i]+1<kol[k] then
      begin
        kol[k]:=kol[i]+1;
        how[k]:=gnome[j];
      end;
  end;
Второй способ – использование запоминающей функции. Чтобы определить, сколько слагаемых потребуется для представления числа `i`, нужно найти вариант с минимальным числом слагаемых после вычитания из `i` одного из гномьих слагаемых. Чтобы повторно не решать одну и ту же задачу, результат расчета запоминаем в массивах kol и how. Вычисление функции, определяющей минимальное число слагаемых, выполняется только в том случае, если оно ранее для числа `i` не выполнялось.
function minkol(i:integer):integer;
var j,k:integer;
begin
  if kol[i]=1000 then
  begin
    if i=0 then
      kol[i]:=0
    else
    begin
      for j:=54 downto 1 do
        if gnome[j]<=i then
        begin
          k:=minkol(i-gnome[j]);
          if k+1<kol[i] then
          begin
            how[i]:=gnome[j];
            kol[i]:=k+1;
          end;
        end;
    end;
  end;
  minkol:=kol[i];
end;
Третий способ – поиск в ширину. В массиве q хранится очередь из чисел, которые нужно обработать. Первый элемент очереди извлекается и все новые числа, которые можно достичь из него прибавлением некоторого слагаемого, отмечаются в массиве kol значением на 1 больше. Достигнутые ранее числа не рассматриваются, а новые числа заносятся в очередь.
var q:array[0..1000000] of integer;
  q1,q2:integer;
...
q1:=0;
q2:=1;
q[0]:=0;
kol[0]:=0;
while q1<q2 do
begin
  i:=q[q1];
  inc(q1);
  for j:=1 to 54 do
  begin
    k:=gnome[j]+i;
    if (k<=1000000) and (kol[k]=1000) then
    begin
      kol[k]:=kol[i]+1;
      how[k]:=gnome[j];
      q[q2]:=k;
      inc(q2);
    end;
  end;
end;
Сумма выводится с использованием массива how, который заполняется во всех трех способах:
write(n,'=');
while n>0 do
begin
  write(how[n]);
  n:=n-how[n];
  if n>0 then write('+');
end;
writeln;
loading