Подразделы

Другие разделы

Дата и время

03/05/2024 03:22:54

Авторизация

Имя:
Пароль:
Зарегистрироваться
Восстановить пароль
 

printРазбор задачи H. Сдача

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

Для сдачи суммы `i` монетами номиналом `b_1,\ …,\ b_j` есть следующие варианты:
  • 0 монет номиналом `b_j`: способ сдать сумму `i` монетами номиналом `b_1,\ …,\ b_{j-1}`;
  • 1 монета номиналом `b_j`: `b_j\ +` способ сдать сумму `i-b_j` монетами номиналом `b_1,\ …,\ b_{j-1}`;

  • `k=\ ⌊\ i//b_j\ ⌋` монет номиналом `b_j`: `k*b_j\ +` способ сдать сумму `i-k*b_j` монетами номиналом `b_1,\ …,\ b_{j-1}`.
Чтобы получить количество способов сдать сумму `i` монетами номиналом `b_1,\ …,\ b_j`, нужно просуммировать количество способов сдать сумму `i-k*b_j` монетами номиналом `b_1,\ …,\ b_{j-1}` для `k` от 0 до `⌊\ i//b_j\ ⌋`.
Количество способов будем хранить в таблице `T[i,\ j]`, где `i` – сумма сдачи, `j` – номер последнего использованного номинала.
`T[0,0]\ =\ 1`
`T[i,0]\ =\ 0`, если `i>0`
`T[i,\ j]\ =\ sum_{k=0}^{⌊\ i//b_j\ ⌋}\ T[i-k*b_j,\ j-1]`, если `j>0`
Вычисления можно существенно ускорить, используя следующее упрощение:
`T[i,\ j]\ =\ sum_{k=0}^{⌊\ i//b_j\ ⌋}\ T[i-k*b_j,\ j-1]\ =\ T[i,j-1]\ +\ sum_{k=1}^{⌊\ i//b_j\ ⌋}\ T[i-k*b_j,\ j-1]\ =\ T[i,j-1]\ +\ sum_{k=0}^{⌊\ i//b_j\ ⌋-1}\ T[i-b_j-k*b_j,\ j-1]\ =\ T[i,j-1]\ +\ T[i-b_j,\ j]`
Вместо матрицы для хранения результатов можно использовать вектор:
`T[i]=T[i]+T[i-b_j]`, для `i` от `b_j` до `S`.
Так как результат может быть очень большим, для вычислений нужно использовать длинную арифметику.
const mysize=25;
type mylong=array[1..mysize] of integer;
{ сложение длинных целых чисел }
procedure add(var a,b:mylong);
var i,p:integer;
begin
  p:=0; { перенос }
  for i:=1 to mysize do
  begin
    p:=p+a[i]+b[i];
    b[i]:=p mod 10;
    p:=p div 10;
  end;
end;
{ вывод длинных целых чисел }
procedure print(var a:mylong);
var i:integer;
    flg:boolean;
begin
  flg:=false; { была ненулевая цифра? }
  for i:=mysize downto 1 do
  begin
    if a[i]<>0 then
      flg:=true;
    if flg or (i=1) then
      write(a[i]);
  end;
end;
Основная часть программы выглядит так:
var T:array[0..10000] of mylong;
    b:array[1..10] of integer;
    i,j,n,s:integer;
...
  read(s,n);
  for i:=1 to n do
    read(b[i]);
  T[0][1]:=1;
  for j:=1 to n do
    for i:=b[j] to s do
      add(T[i-b[j]],T[i]);
  print(T[s]);
loading