Автомат с монетами: когда жадность ошибается

тема: Жадные алгоритмы · уровень: средний

Условие

В школьном буфете поставили автомат, который выдаёт сдачу монетами. Он работает «жадно»: всегда берёт самую крупную монету, которая не превышает оставшуюся сумму, и повторяет.

Иногда такой алгоритм даёт лишние монеты (хотя можно было разменять той же суммой меньшим числом монет).

Найди самую маленькую сумму x (в пределах от 0 до A), для которой жадный алгоритм выдаёт строго больше монет, чем оптимальный размен. Если такой суммы нет, выведи -1.

Важно: номиналы могут быть очень большими (как 64-битные целые на олимпиадах), но сумма A ограничена.

Формат ввода

В первой строке даны два целых числа n и A — количество номиналов и максимальная проверяемая сумма. Во второй строке даны n различных целых чисел c1, c2, ..., cn — номиналы монет.

Формат вывода

Выведи одно целое число — минимальную сумму x (0 ≤ x ≤ A), где жадный размен хуже оптимального, или -1, если жадный алгоритм оптимален для всех сумм от 0 до A.

Ограничения

Пример

Ввод:

4 28
1 2 5 10

Вывод:

-1

(Для всех сумм до 28 жадный размен даёт минимальное число монет.)

Как решать — идея подхода

Приём: ДП для минимального числа монет + проверка жадного

Ключевое наблюдение: если жадный алгоритм хоть где-то ошибается, то существует контрпример среди сумм меньше c[n-1] + c[n] (две самые большие монеты). Значит, не нужно проверять все огромные суммы и тем более зависеть от величин ci (они могут быть до 1e12) — достаточно ограничиться limit = min(A, c[n] + c[n-1] - 1).

Дальше делаем два независимых подсчёта для всех x от 1 до limit:

План:

Сложность: O(n * limit) по времени и O(limit) по памяти, при n <= 20 и limit <= 1e6 это проходит.

Частая ошибка: строить DP до A, забывая про ограничение c[n-1] + c[n], или пытаться учитывать монеты > limit (они не могут участвовать в суммах до limit).

Разберись руками

Монеты: 1, 2, 5, 10. Автомат считает сдачу жадно: берёт самую большую монету, которая влезает, и так пока не станет 0. Нужно найти самую маленькую сумму от 0 до 28, где жадный даст больше монет, чем можно на самом деле, или понять, что такой суммы нет.

Идея: Сначала замечаем, что жадный размен по-любому набирает десятки, а дальше остаётся остаток меньше 10. Чтобы понять, может ли жадный проиграть на суммах до 28, достаточно посмотреть на такие остатки и проверить самые «опасные» из них — где жадный тратит 3 монеты и теоретически мог бы быть вариант в 2 монеты. Если даже там улучшения нет, то и для всех сумм до 28 жадный не хуже оптимального.

Решить задачу с автопроверкой на Python →

Куда дальше