Maybe you wanted us to fix your code, but instead I implemented my own version of solution. Hopefully my own version will be useful somehow for you, at least educationally.
Of course I used Dynamic Programming approach for that.
I keep a vector of possible to compose changes. Each next sums is composed of previous sums by adding several coins of same value.
History of used coins is also kept, this allows us to restore each change as combination of exactly given coins.
After code you can see console output that shows example of composing change 13 out of coins 2x4, 3x3, 5x2, 10x1 (here second number is amount of coins).
Input coins and their amount is given inside coins vector at start of main() function, you can fill this vector with anything you want, for example by taking console user input. Needed to be represented change is given inside variable change.
Don't forget to see Post Scriptum (PS.) after code and console output, it has some more details about algorithm.
Full code below:
Try it online!
#include <cstdint>
#include <vector>
#include <unordered_map>
#include <set>
#include <algorithm>
#include <functional>
#include <iostream>
using u32 = uint32_t;
using u64 = uint64_t;
int main() {
    std::vector<std::pair<u32, u32>> const coins =
        {{2, 4}, {3, 3}, {5, 2}, {10, 1}};
    u32 const change = 13;
    std::vector<std::unordered_map<u32, std::pair<u64, std::set<u32>>>>
        sums = {{{0, {1, {}}}}};
    for (auto [coin_val, coin_cnt]: coins) {
        sums.push_back({});
        for (auto const & [k, v]: sums.at(sums.size() - 2))
            for (size_t icnt = 0; icnt <= coin_cnt; ++icnt) {
                auto & [vars, prevs] = sums.back()[k + coin_val * icnt];
                vars += v.first;
                prevs.insert(icnt);
            }
    }
    std::vector<std::pair<u32, u32>> path;
    std::vector<std::vector<std::pair<u32, u32>>> paths;
    std::function<bool(u32, u32, u32)> Paths =
        [&](u32 sum, u32 depth, u32 limit){
            if (sum == 0) {
                paths.push_back(path);
                std::reverse(paths.back().begin(), paths.back().end());
                return paths.size() < limit;
            }
            auto const coin = coins.at(depth - 1).first;
            auto const & [_, prevs] = sums.at(depth).at(sum);
            for (auto const cnt: prevs) {
                if (cnt > 0)
                    path.push_back({coin, cnt});
                if (!Paths(sum - coin * cnt, depth - 1, limit))
                    return false;
                if (cnt > 0)
                    path.pop_back();
            }
            return true;
        };
    if (!sums.back().count(change)) {
        std::cout << "Change " << change
            << " can NOT be represented." << std::endl;
        return 0;
    }
    std::cout << "Change " << change << " can be composed "
        << std::get<0>(sums.back().at(change)) << " different ways." << std::endl;
    Paths(change, coins.size(), 20);
    std::cout << "First " << paths.size() << " variants:" << std::endl;
    for (auto const & path: paths) {
        std::cout << change << " = ";
        for (auto [coin, cnt]: path)
            std::cout << coin << "x" << cnt << " + ";
        std::cout << std::endl;
    }
}
Output:
Change 13 can be composed 5 different ways.
First 5 variants:
13 = 2x2 + 3x3 + 
13 = 2x4 + 5x1 + 
13 = 2x1 + 3x2 + 5x1 + 
13 = 3x1 + 5x2 + 
13 = 3x1 + 10x1 + 
PS. As you may have noticed, main Dynamic Programming part of algorithm is very tiny, just following lines:
    std::vector<std::unordered_map<u32, std::pair<u64, std::set<u32>>>>
        sums = {{{0, {1, {}}}}};
    for (auto [coin_val, coin_cnt]: coins) {
        sums.push_back({});
        for (auto const & [k, v]: sums.at(sums.size() - 2))
            for (size_t icnt = 0; icnt <= coin_cnt; ++icnt) {
                auto & [vars, prevs] = sums.back()[k + coin_val * icnt];
                vars += v.first;
                prevs.insert(icnt);
            }
    }
This part keeps all currently composable sums (changes). Algo starts from money change of 0, then incrementally adds 1-by-1 coin to all possible current changes (sums), thus forming new sums (including this new coin).
Each sum keeps a counter of all possible ways to compose it plus it keeps track of all last coins that lead to this sum. This last coins set allows to do back-tracking in order to restore concrete combinations of coins, not just amount of ways to compute this sum.