Здравствуйте! Я столкнулся с известной задачей о сдаче из бесконечного количества монет определенного номинала. Недавно я наткнулся на аналогичную задачу, где у меня есть монеты с известными номиналами, но в ограниченном количестве. В связи с этим, мне нужно определить, сколько раз я могу собрать определенную сумму из данного набора монет, учитывая, что при каждом наборе взятые монеты вычитаются из общего количества. Предположим, что у меня есть номиналы монет a, b, c и соответствующее количество монет x, y, z для каждого номинала, а также сумма e, которую нужно набрать. Это можно выразить так: x * a + y * b + z * c = e. Я думаю, что можно применить бинарный поиск по ответу, но не уверен, как лучше организовать процесс выбора нужных монет и как удостовериться, что я получу наибольшее количество возможных наборов. Как мне поступить в данной ситуации?
Задача, которую вы описали, является классической задачей о рюкзаке с ограничениями по количеству предметов. Ваша цель — собрать заданную сумму из монет ограниченного количества, что можно решить с помощью динамического программирования или других методов. Одним из наиболее эффективных способов решения этой задачи является использование подхода динамического программирования для алгоритма "вместимость рюкзака". Вот шаги, которые вы можете использовать: ### Подход с динамическим программированием: 1. **Масштабирование задачи:** Создайте массив `dp`, где `dp[j]` будет представлять максимальное количество раз, когда вы можете собрать сумму `j` с использованием известных монет. 2. **Инициализация:** Установите `dp[0] = 1` (вы можете собрать сумму 0 одним способом — использовать 0 монет) и остальные элементы массива `dp` равными 0. 3. **Наполнение массива `dp`:** Пройдите по каждому типу монет. Для каждой монеты, учитывая ее количество, обновите массив `dp`: - Для каждой суммы от `e` до значения номинала (в обратном порядке) проверьте, можно ли улучшить текущие значения с учетом количества доступных монет. - Используйте вложенный цикл, который будет учитывать количество монет для каждого номинала. 4. **Получение результата:** В конце вы получите значение `dp[e]`, которое будет представлять максимальное количество раз, которые вы можете собрать сумму `e` с использованием имеющихся монет. ### Пример реализации на Python: ```python def max_combinations(a, b, c, x_a, x_b, x_c, e): denominations = [(a, x_a), (b, x_b), (c, x_c)] dp = [0] * (e + 1) dp[0] = 1 # 1 способ собрать 0 сумма for value, quantity in denominations: for j in range(e, value - 1, -1): for k in range(1, quantity + 1): if j >= k * value: dp[j] += dp[j - k * value] else: break return dp[e] # Пример использования a = 1 b = 2 c = 5 x_a = 5 x_b = 2 x_c = 1 e = 10 result = max_combinations(a, b, c, x_a, x_b, x_c, e) print("Максимальное количество способов собрать сумму", e, ":", result) ``` ### Объяснение кода: - Мы определяем номиналы монет и их количество. - Создаем массив `dp`, где значения обновляются в зависимости от того, сколько монет мы можем использовать. - В цикле мы обновляем массив `dp` для каждой монеты, учитывая, сколько раз можем ее использовать. Таким образом, вы сможете найти максимальное количество способов собрать необходимую сумму при помощи заданных монет.
Задача по смыслу очень похожа на укладку рюкзака ( Knapsack_problem )
https://en.wikipedia.org/wiki/Knapsack_problem
Можно целочисленным линейным программированием решать. Бинплиском перебтрайте ответ (k), потом составляйте задачу, где у вас переменные: сколько монет каждого типа берём в каждый из k разменов. И еще будут неравенства, что каждый тип монет используется суммарно не больше, чем есть монет. Целевая функция - любая.
Еще можно чуть по-другому: находите переблром все варианты одного размена. Потом у вас переменные будут, сколько раз каждый из вариантов берете. Ограничения на общий расход каждой монеты, а целевая функция - сумма переменных. Тут нет бинплиска, но тоже ЦЛП.
В лоб - составить все возможные варианты набора определённой суммы, затем определить непересекающиеся группы наборов и для каждой группы рекурсивно перебирать все комбинации.
Скажем, номиналы 5, 4, 3, 2, необходимая сумма 8
Получаем наборы (5, 3), (4, 4), (4, 2, 2), (3, 3, 2), (2, 2, 2, 2).
Одна группа, в которую входят все наборы.
Номиналы 5, 4, 3, необходимая сумма 8
Получаем наборы (5, 3), (4, 4).
Две группы [(5, 3)] и [(4, 4)]
Для каждой группы.
Начиная с 0 смотрим, в цикле можем ли мы ещё раз взять первый набор. Если да, то рекурсивно смотрим, сколько раз можно набрать нужную сумму остальными наборами из группы из остатка монет. на каждом шаге цикла суммируем количество первых наборов и вернувшееся количество наборов из остатков. Ищем максимум этой суммы.
Пройдя по всем группам суммируем результаты, получаем общее число раз.
пусть a, b, c - номиналы монет, x, y, z - количество монет каждого номинала и e - сумма которую нужно набрать. Получается выражение x * a + y * b + z * c = e.
- это не правильное выражение (точнее оно будет верным только в частном случае, когда искомая сумма будет равна количеству всех имеющихся денег)
x[i] * a + y[j] * b + z[k] * c = e - вот это правильное
Что мы знаем из входных данных? мы знаем сколько у нас есть денег всего, это как раз x * a + y * b + z * c, пусть это будет N;
Дальше мы знаем сумму которую необходимо набрать это e;
Нам нужно понять сколько раз мы можем получить e из N, пусть это будет Q
N / e = M - количество способов получить e математически
Это нам не подходит так как нам надо учитывать номиналы, но теперь у нас есть ограничение
0 <= Q <= M
это к вопросукак убедиться что я получу наибольшее количество
Дальше будет вариант решения задачи неоптимальным способом "как получится" (смотри статус профиля) более правильным вариантом будет почитать другие ответы на вопрос и составить собственный вариант решения задачи с учетом теории из линейного программирования
1. понять, какие номиналы будут участвовать в получение e (и есть ли они у нас в целом)
2. отсечь случай e === N
3. получить все комбинации для получения e
4. понять какие из них сочитаются между собой с учетом ограниченности монет
5. найти наибольшее количество сочитаемых
Дальше предоставляю говнокод на js который решает эту задачу как минимум в частных случаях (я проверил парочку вариантов входных параметров)