将低效的递归硬币更改功能转换为迭代

时间:2018-06-21 12:08:44

标签: c++ recursion coin-change

我有一个效率低下的递归硬币兑换功能,可以计算出给定数量的硬币组合数量。如果可能的话,我想将其转换为更有效的迭代函数。

一个问题是,我正在使用回溯法尝试一种叫做面额的数组中的不同硬币。我也在使用备忘录,但是当金额很大时,它并不会加快速度。

这是我的代码:

unsigned long long CalculateCombinations(std::vector<double> &denominations, std::vector<double> change,
    double amount, unsigned int index)
{
    double current = 0.0;
    unsigned long long combinations = 0;

    if (amount == 0.0)
    {
        if (change.size() % 2 == 0)
        {
            combinations = Calculate(change);
        }
        return combinations;
    }

    // If amount is less than 0 then no solution exists
    if (amount < 0.0)
        return 0;

    // If there are no coins and index is greater than 0, then no solution exist
    if (index >= denominations.size())
        return 0;

    std::string str = std::to_string(amount) + "-" + std::to_string(index) + "-" + std::to_string(change.size());

    auto it = Memo.find(str);

    if (it != Memo.end())
    {
        return it->second;
    }

    while (current <= amount)
    {
        double remainder = amount - current;
        combinations += CalculateCombinations(denominations, change, remainder, index + 1);
        current += denominations[index];
        change.push_back(denominations[index]);
    }

    Memo[str] = combinations;
    return combinations;
}

有什么想法可以做到吗?我知道有针对硬币找零问题的DP解决方案,但我的解决方案并不容易。我可以有半便士。

*更新:我将该函数更改为迭代函数,并按比例放大2倍以使用整数,但它没有太大的区别。

这是我的新代码:

unsigned long long CalculateCombinations(std::vector<int> &denominations, std::vector<int> change, int amount, unsigned int index)
{
    unsigned long long combinations = 0;

    if (amount <= 0)
        return combinations;

    std::stack<Param> mystack;
    mystack.push({ change, amount, index });

    while (!mystack.empty())
    {
        int current = 0;
        std::vector<int> current_coins = mystack.top().Coins;
        int current_amount = mystack.top().Amount;
        unsigned int current_index = mystack.top().Index;
        mystack.pop();

        if (current_amount == 0)
        {
            if (current_coins.size() % 2 == 0)
            {
                combinations += Calculate(std::move(current_coins));
            }
        }
        else
        {
            std::string str = std::to_string(current_amount) + "-" + std::to_string(current_index);
            if (Memo.find(str) == Memo.end())
            {
                // If amount is less than 0 then no solution exists
                if (current_amount >= 0 && current_index < denominations.size())
                {
                    while (current <= current_amount)
                    {
                        int remainder = current_amount - current;
                        mystack.push({ current_coins, remainder, current_index + 1 });
                        current += denominations[current_index];
                        current_coins.push_back(denominations[current_index]);
                    }
                }
                else
                {
                    Memo.insert(str);
                }
            }
        }
    }

    return combinations;
}

备忘录定义为std :: unordered_set。

这可以由DP解决吗?问题是我对所有组合都不感兴趣-仅对大小相等的组合感兴趣。

1 个答案:

答案 0 :(得分:0)

我在您的代码中看不到任何丢弃面额的策略。

我的递归答案将是在每个递归阶段创建2个子代:
1个孩子使用完整的面额列表,并花费1个最终面额
第二个孩子放弃相同的结束面额

他们每个人都递归,但是在第二种情况下,孩子们可以少用一个教派。

我相信返回的结果都是截然不同的,但当然您会遇到痛苦的情况,您需要递归10000个级别,才能获得100美分的美分。当您减少到1个面额时,可以很容易地对其进行优化,并且可能表明最好在每个回合中处理和丢弃较高面额而不是较低面额的硬币。

您还可以检测到所有剩余面额都是彼此简单倍数的情况,无需进行完整递归即可快速生成排列:生成最小硬币集(每个高面额的最大值),然后向后工作以数字替换每个硬币小硬币。