1

Тема: Комбінації чисел для заданої суми

Умови завдання такі.
Є певна сума грошей і монети різного номіналу.
Треба знайти всі можливі способи розміняти цю суму монетами.
Кількість монет необмежена.
Наприклад, сума 4, монети {1, 2}.
Комбінації монет {1,1,1,1} , {1, 1, 2}, {2, 2}.
Я пробував зробити щось таке:

unsigned long long countChange(const unsigned int money,
    const std::vector<unsigned int>& coins)
{
   
    int arr_size = coins.size();
    std::vector<unsigned int> new_coins = coins;
    unsigned long long answer { 0 };
    int money_amount = money;
    std::sort(new_coins.begin(), new_coins.end());
    do {
        for (int i = money_amount; i >= 0; --i) {
            for (int j = 0; j < arr_size; ++j) {
                cout << new_coins.at(j) * i << " ";
            }
            cout << endl;
        }
    } while (std::next_permutation(new_coins.begin(), new_coins.end()));
    return answer;
}

Застряг на комбінаціях, розумію, що треба брати монети
в різних кількостях і перебирати все це, але не знаю як у коді реалізувати без рекурсії.

2

Re: Комбінації чисел для заданої суми

Рекурсію можна замінити циклом і стеком. Стек (чи радше вектор) буде представляти кількість монет кожного номіналу, відповідно до coins.
Кладете в стек money/coins[0] монет, віднімаєте від money stack[0]*coins[0].
А далі:

- ідете по coins, якщо money більше за якесь зі значень у coins - кладете в стек це значення по максимуму (проміжні значення записуєте нулі), якщо money = 0 - виводите.
- якщо жодними монетами не виходить представити money - берете останнє число у стеку, якщо це 0, викидаєте і повторюєте, якщо ні - зменшуєте його на 1, додаєте відповідне значення до money. Якщо вичерпали стек - кінець алгоритму.

Сума 4, монети {1,2}.
Початковий стан стеку {4}.
Додаємо до {4,0}, money = 0, виводимо.
Викидаємо 0, віднімаємо 1 {3}.
Додаємо до {3,0}, money=1, coins скінчилися, не виводимо.
Викидаємо 0, віднімаємо 1 {2}
Додаємо до {2,1}, money = 0, виводимо
Віднімаємо 1, {2,0}, money = 2, coins скінчилися, не виводимо.
Викидаємо 0, віднімаємо 1 {1}
Додаємо до {1,1}, money = 1, coins скінчилися. не виводимо
Віднімаємо {1,0}, money = 3, coins скінчилися. не виводимо
Викидаємо 0, віднімаємо 1, {0}
Додаємо до {0,2}, виводимо
Віднімаємо {0,1}, money = 2, coins скінчилися. не виводимо
Віднімаємо {0,0}, money = 4, coins скінчилися. не виводимо
Викидаємо обидва нулі, стек скінчився, кінець.

Ніби працює.

3

Re: Комбінації чисел для заданої суми

koala написав:

Рекурсію можна замінити циклом і стеком. Стек (чи радше вектор) буде представляти кількість монет кожного номіналу, відповідно до coins.
Кладете в стек money/coins[0] монет, віднімаєте від money stack[0]*coins[0].
А далі:

- ідете по coins, якщо money більше за якесь зі значень у coins - кладете в стек це значення по максимуму (проміжні значення записуєте нулі), якщо money = 0 - виводите.
- якщо жодними монетами не виходить представити money - берете останнє число у стеку, якщо це 0, викидаєте і повторюєте, якщо ні - зменшуєте його на 1, додаєте відповідне значення до money. Якщо вичерпали стек - кінець алгоритму.

Сума 4, монети {1,2}.
Початковий стан стеку {4}.
Додаємо до {4,0}, money = 0, виводимо.
Викидаємо 0, віднімаємо 1 {3}.
Додаємо до {3,0}, money=1, coins скінчилися, не виводимо.
Викидаємо 0, віднімаємо 1 {2}
Додаємо до {2,1}, money = 0, виводимо
Віднімаємо 1, {2,0}, money = 2, coins скінчилися, не виводимо.
Викидаємо 0, віднімаємо 1 {1}
Додаємо до {1,1}, money = 1, coins скінчилися. не виводимо
Віднімаємо {1,0}, money = 3, coins скінчилися. не виводимо
Викидаємо 0, віднімаємо 1, {0}
Додаємо до {0,2}, виводимо
Віднімаємо {0,1}, money = 2, coins скінчилися. не виводимо
Віднімаємо {0,0}, money = 4, coins скінчилися. не виводимо
Викидаємо обидва нулі, стек скінчився, кінець.

Ніби працює.

Кількість монет кожного номіналу необмежена. Тому не думаю, що ваш варіант спрацює.

4

Re: Комбінації чисел для заданої суми

А які обмеження на money/coins[0]?

5

Re: Комбінації чисел для заданої суми

koala написав:

А які обмеження на money/coins[0]?

Комбінації мають бути унікальними, наприклад, {2, 3} i {3, 2} — це одна комбінації.
Тобто порядок не має значення.
Це завдання звідси https://www.codewars.com/kata/541af676b589989aed0009e7

6

Re: Комбінації чисел для заданої суми

Перечитайте, ЩО саме в стеку.
Я робив рекурсією, до речі.

7

Re: Комбінації чисел для заданої суми

Задача про розбиття (integer partition)

зображеня для розуміня ідеї (*.svg)

https://upload.wikimedia.org/wikipedia/commons/4/46/Partitions_of_n_with_biggest_addend_k.svg

Подібний топік.

8

Re: Комбінації чисел для заданої суми

Ось чим усе скінчилося:

unsigned long long countChange(const unsigned int money,
    const std::vector<unsigned int>& coins)
{
    
    int coins_amount = coins.size();
    std::vector<unsigned long long> answer(money + 1, 0);
    answer.at(0) = 1;
    for (int i = 0; i < coins_amount; ++i) {
        for (unsigned int j = coins.at(i); j <= money; ++j) {
            answer.at(j) += answer.at(j - coins.at(i));
        }
    }
    for (auto& sol : answer) {
        std::cout << sol << " ";
    }
    std::cout << std::endl;

    return answer.at(money);
}

До речі, у тому завданні рекурсія не приймається як відповідь, хоч там і є позначка "recursion" знизу умови завдання.
Якщо подати рекурсію, пише, що занадто довго, поганий алгоритм:).

Подякували: koala1