【问题标题】:Coin changing algorithm硬币兑换算法
【发布时间】:2009-12-31 19:36:17
【问题描述】:

假设我有一组面额为 a1、a2、... ak 的硬币。

其中一个已知等于 1。

我想用最少硬币数为所有整数 1 到 n 找零。

算法的任何想法。

eg. 1, 3, 4 coin denominations
n = 11
optimal selection is 3, 0, 2 in the order of coin denominations.

n = 12
optimal selection is 2, 2, 1.

注意:不是作业只是this问题的修改

【问题讨论】:

  • 帮助某人解决一个家庭作业问题并不会突然让他们成为 A+ 学生。在某些情况下,它可能会帮助那个学生“看到光明”并成长为一个聪明的年轻开发者。然而,重复这种行为的人(不试图自己解决问题)更有可能只是一个永远不会成长的人,因为他们没有挑战自己。他们会在某个时候崩溃并痛苦地燃烧,最有可能是考试日。至少在我上学的地方,考试占我们成绩的很大一部分,以至于家庭作业实际上是无关紧要的(在一门课程中考试是 100%)。

标签: algorithm


【解决方案1】:

这是一个经典的动态规划问题(首先请注意,贪心算法在这里并不总是有效!)。

假设硬币的顺序是a_1 > a_2 > ... > a_k = 1。我们定义了一个新问题。我们说(i, j) 问题是使用硬币a_i > a_(i + 1) > ... > a_k 找到为j 找零的最小硬币数量。我们希望解决的问题是(1, j) 用于任何j1 <= j <= n。假设C(i, j)(i, j) 问题的答案。

现在,考虑一个实例(i, j)。我们必须决定是否使用a_i 硬币之一。如果不是,我们只是在解决(i + 1, j) 问题,答案是C(i + 1, j)。如果是,我们通过更改j - a_i 来完成解决方案。为了使用尽可能少的硬币来做到这一点,我们想要解决(i, j - a_i) 问题。我们安排好这两个问题已经为我们解决了,然后:

C(i, j) = C(i + 1, j)                         if a_i > j
        = min(C(i + 1, j), 1 + C(i, j - a_i)) if a_i <= j

现在弄清楚最初的案例是什么,以及如何将其翻译成您选择的语言,您应该一切顺利。

如果您想尝试解决另一个需要动态编程的有趣问题,请查看 Project Euler Problem 67

【讨论】:

  • 为什么贪婪的方法总是行不通,我不明白?
  • @hhafez:考虑为30 换取硬币面额{1, 10, 20, 25}。贪心算法产生{25, 1, 1, 1, 1, 1},但最优解是{20, 10}。我认为贪婪算法起作用的硬币集的术语是“友好的硬币集”。确定一个硬币组是否友好是一个有趣的问题。我可能用错了术语,但无论哪种方式,问题都很有趣。
【解决方案2】:

以下是 Python 中动态编程算法的示例实现。它比 Jason 描述的算法更简单,因为它只计算他描述的 2D 表的 1 行。

请注意,使用此代码作弊会使 Zombie Dijkstra 哭泣。

import sys
def get_best_coins(coins, target):
    costs = [0]
    coins_used = [None]
    for i in range(1,target + 1):
        if i % 1000 == 0:
            print '...', 
        bestCost = sys.maxint
        bestCoin = -1
        for coin in coins:
            if coin <= i:
                cost = 1 + costs[i - coin]
                if cost < bestCost:
                    bestCost = cost
                    bestCoin = coin
        costs.append(bestCost)
        coins_used.append(bestCoin)
    ret = []    
    while target > 0:
        ret.append(coins_used[target])
        target -= coins_used[target]
    return ret

coins = [1,10,25]
target = 100033
print get_best_coins(coins, target)

【讨论】:

    【解决方案3】:

    以下是自下而上的动态规划方法。

            int[] dp = new int[amount+ 1]; 
            Array.Fill(dp,amount+1);
            dp[0] = 0;
            for(int i=1;i<=amount;i++)
            {
                for(int j=0;j<coins.Length;j++)
                {
                    if(coins[j]<=i) //if the amount is greater than or equal to the current coin
                    {
                        //refer the already calculated subproblem dp[i-coins[j]]
                        dp[i] = Math.Min(dp[i],dp[i-coins[j]]+1);
                    }
                }
            }
            if(dp[amount]>amount)
                return -1;
            return dp[amount];
        }
    

    【讨论】:

    • 感谢您的好回答。您也想发布它的复杂性吗?
    • 时间复杂度:O(S*n),空间复杂度:O(n) 其中n:数量,S:硬币数量
    猜你喜欢
    • 1970-01-01
    • 2021-10-01
    • 2012-09-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多