Я недавно обсуждал с коллегой следующую (вполне базовую c) проблему и хотел бы знать, как ее оптимизировать и во время выполнения оптимизированной версии.
Проблема:
Вы должны вернуть сумму денег с доступным набором монет. У вас есть неограниченное количество монет. Если невозможно вернуть эту сумму, вы должны вернуть ошибку.
Пример 1:
Набор монет (в данном случае евро, в кт) {200, 100, 50, 20, 10, 5, 2, 1}
, сумма возврата 99
.
Ответ: 50, 20, 20, 5, 2, 2
Пример 2:
Набор монет {50, 20, 2}
, сумма возврата 99
.
Ответ: no solution found!
(очевидно, поскольку невозможно вернуть нечетное число!).
Мое решение:
(код в конце этого поста). Мое решение - это рекурсивная функция, которая жадная, но попытается использовать меньшие монеты, если жадный подход не работает. В конце концов, я в основном пересекаю пространство решений (дерево) в глубину.
В худшем случае (например, {97, 98, 99}
и 300
) решение не найдено, и я должен пройти по всему дереву, что приводит к O(l^(a\s))
, где l
- длина набора монет (8 в случае EUR, 3 в этом случае), a
- сумма (300 в примерах) s
- наименьшая из доступных монет (в данном примере 97), а \
- целочисленное деление . Пожалуйста, исправьте меня, если я ошибаюсь .
Оптимизация, которую я ищу:
Вы видите, что есть некоторые пути в " дерево решений ", которые не нужны для изучения. Это связано с тем, что порядок монет не имеет значения, но различные порядки все еще проходят (например, 99, 98, 97
и 98, 99, 97
- оба). Как я могу обрезать дерево таким образом, чтобы обходились только необходимые пути? Я думал о каком-то кешировании, но не могу придумать что-нибудь умное. Какой будет среда выполнения такого решения?
C ++ 17 Код:
#include <iostream>
#include <list>
#include <optional>
#include <string>
std::string print_list(std::optional<std::list<int>> lst)
{
if (lst)
{
std::string res = "";
for (int num : lst.value())
{
res += std::to_string(num) + ", ";
}
return res + "\n";
}
return "no solution found!\n";
}
std::optional<std::list<int>> get_coins(int remainder, std::list<int> ¤cy, std::list<int> coins)
{
if (remainder == 0)
{
return std::optional{coins};
}
for (int coin : currency)
{
if (coin <= remainder)
{
coins.push_back(coin);
auto result = get_coins(remainder - coin, currency, coins);
if (result)
{
return result;
}
}
}
return std::nullopt;
}
int main()
{
// Example 1
std::list<int> eur{200, 100, 50, 20, 10, 5, 2, 1};
std::cout << print_list(get_coins(99, eur, {})); // 50, 20, 20, 5, 2, 2,
// Example 2
std::list<int> fail{50, 20, 2};
std::cout << print_list(get_coins(99, fail, {})); // no solution found!
}