【问题标题】:Minimum number of coins for a given sum and denominations给定金额和面额的最小硬币数量
【发布时间】:2016-11-25 12:00:24
【问题描述】:

给定一组面额和所需的总和,我必须找到构成该总和的最小硬币数量以及每个面额的硬币数量

请帮忙!!

【问题讨论】:

    标签: dynamic-programming coin-change


    【解决方案1】:

    确定所需的最小硬币数量的伪代码是:

    Procedure coinChange(coins, total):
    n := coins.length
    dp[n][total + 1]
    for i from 0 to n
        dp[i][0] := 0
    end for
    for i from 1 to (total + 1)
        dp[0][i] := i
    end for
    for i from 1 to n
        for j from 1 to (total + 1)
            if coins[i] > j                 //if the denomination is greater than total
                dp[i][j] := dp[i-1][j] 
            else                           //if the denomination is less than or equal to total
                dp[i][j] := min(dp[i-1][j], dp[i][j-coins[i]])
            end if
        end for
    end for
    Return dp[n-1][total]
    

    并找出所需的面额:

    Procedure printChange(coins, dp, total):
    i := coins.length - 1
    j := total
    min := dp[i][j]
    while j is not equal to 0
        if dp[i-1][j] is equal to min          //if the value came from the top we didn't choose current coin
            i := i - 1
        else
            Print(coins[i])
            j := j - coins[i]
        end if
    end while
    

    如果要打印每个面额的数字,在打印coins[i]之后,需要打印dp[j]dp[j - coins[i]]的差值。其余代码将相同。

    此解决方案的完整描述曾经在 SO Docs 中找到,但现在已移至此处。


    硬币兑换问题

    获得总数的最少硬币数

    给定不同面额的硬币和总数,如果我们使用最少数量的硬币,我们需要合并多少硬币才能得到总数?假设我们有coins = {1, 5, 6, 8}total = 11,我们可以使用2 个硬币得到总数,即{5, 6}。这确实是获得11所需的最小硬币数量。我们还将假设有无限的硬币供应。我们将使用动态规划来解决这个问题。

    我们将使用一个二维数组 dp[n][total + 1],其中 n 是我们拥有的不同面额硬币的数量。对于我们的示例,我们需要 dp[4][12]。这里 dp[i][j] 将表示如果我们有 coins[0] 的硬币,则获得 j 所需的最小硬币数硬币[i]。例如,如果我们有 coins[0]coins[1]dp[1][2] 将存储,最小数量是多少我们可以用来制作2的硬币。让我们开始吧:

    首先,对于第0列,完全不拿任何硬币可以使0。所以第0列的所有值都是0。对于 dp[0][1],我们问自己是否只有 1 面额的硬币,即 coins[0] = 1,获得1所需的最小硬币数量是多少?答案是1。对于 dp[0][2],如果我们只有 1,获得 2 所需的最小硬币数是多少。答案是2。类似地 dp[0][3] = 3, dp[0][4] = 4 等等在。这里要提到的一件事是,如果我们没有面额为 1 的硬币,在某些情况下,使用 1 硬币可能无法达到总数只要。为简单起见,我们在示例中采用 1。第一次迭代后,我们的 dp 数组将如下所示:

            +---+---+---+---+---+---+---+---+---+---+---+---+---+
     (denom)|   | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10| 11|
            +---+---+---+---+---+---+---+---+---+---+---+---+---+
        (1) | 0 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10| 11|
            +---+---+---+---+---+---+---+---+---+---+---+---+---+
        (5) | 1 | 0 |   |   |   |   |   |   |   |   |   |   |   |
            +---+---+---+---+---+---+---+---+---+---+---+---+---+
        (6) | 2 | 0 |   |   |   |   |   |   |   |   |   |   |   |
            +---+---+---+---+---+---+---+---+---+---+---+---+---+
        (8) | 3 | 0 |   |   |   |   |   |   |   |   |   |   |   |
            +---+---+---+---+---+---+---+---+---+---+---+---+---+
    

    继续前进,对于 dp[1][1],我们问自己是否有 coins[0] = 1coins[1] = 5,获得1所需的最小硬币数量是多少?由于 coins[1] 大于我们当前的总数,因此不会影响我们的计算。我们需要排除 coins[5] 并仅使用 coins[0] 获得 1。该值存储在 dp[0][1] 中。所以我们从顶部取值。我们的第一个公式是:

    if coins[i] > j
        dp[i][j] := dp[i-1][j]
    

    这个条件一直成立,直到我们在 dp[1][5] 的总数为 5,对于这种情况,我们可以使 5 两种方式: - 我们采用 5 个面额的 coins[0],存储在 dp[0][5] 上(从顶部开始)。 - 我们采用 1 面额的 coins[1] 和 (5 - 5) = 0 硬币[0]的面额。

    我们将选择这两者中的最小值。所以 dp[1][5] = min(dp[0][5], 1 + dp[1][ 0]) = 1。为什么我们在这里提到 0 面额的 coins[0] 将在我们的下一个位置中显而易见。

    对于dp[1][6],我们可以通过两种方式制作6: - 我们采用 6 面额的 coins[0],存储在顶部。 - 我们可以取51面额,我们需要6 - 5 = 1 得到总数。使用15面额的硬币获得1的最少方法存储在dp[1][1]中,可以写成dp[i][j-coins[i]],其中i = 1。这就是我们以这种方式编写先前值的原因。

    我们将采取这两种方式中的最低限度。所以我们的第二个公式是:

    if coins[i] >= j
        dp[i][j] := min(dp[i-1][j], dp[i][j-coins[i]])
    

    使用这两个公式,我们可以填满整个表格。我们的最终结果将是:

            +---+---+---+---+---+---+---+---+---+---+---+---+---+
     (denom)|   | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10| 11|
            +---+---+---+---+---+---+---+---+---+---+---+---+---+
        (1) | 0 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10| 11|
            +---+---+---+---+---+---+---+---+---+---+---+---+---+
        (5) | 1 | 0 | 1 | 2 | 3 | 4 | 1 | 2 | 3 | 4 | 5 | 2 | 3 |
            +---+---+---+---+---+---+---+---+---+---+---+---+---+
        (6) | 2 | 0 | 1 | 2 | 3 | 4 | 1 | 1 | 2 | 3 | 4 | 2 | 2 |
            +---+---+---+---+---+---+---+---+---+---+---+---+---+
        (8) | 3 | 0 | 1 | 2 | 3 | 4 | 1 | 1 | 2 | 1 | 2 | 2 | 2 |
            +---+---+---+---+---+---+---+---+---+---+---+---+---+
    

    我们需要的结果将存储在 dp[3][11]。程序将是:

    Procedure coinChange(coins, total):
    n := coins.length
    dp[n][total + 1]
    for i from 0 to n
        dp[i][0] := 0
    end for
    for i from 1 to (total + 1)
        dp[0][i] := i
    end for
    for i from 1 to n
        for j from 1 to (total + 1)
            if coins[i] > j
                dp[i][j] := dp[i-1][j] 
            else
                dp[i][j] := min(dp[i-1][j], dp[i][j-coins[i]])
            end if
        end for
    end for
    Return dp[n-1][total]
    

    该算法的运行时间复杂度为:O(n*total) 其中n是硬币面额的数量。

    要打印所需的硬币,我们需要检查: - 如果值来自顶部,则不包括当前硬币。 - 如果值来自左侧,则包括当前硬币。

    算法是:

    Procedure printChange(coins, dp, total):
    i := coins.length - 1
    j := total
    min := dp[i][j]
    while j is not equal to 0
        if dp[i-1][j] is equal to min
            i := i - 1
        else
            Print(coins[i])
            j := j - coins[i]
        end if
    end while
    

    对于我们的示例,方向将是:

    值将是 65

    【讨论】:

      【解决方案2】:

      代码中有3处错别字:

      第一:

      if coins[i] >= j 
          dp[i][j] := min(dp[i-1][j], dp[i][j-coins[i]])
      

      这应该是:

       if coins[i] > j <-- HERE
              dp[i][j] := min(dp[i-1][j], dp[i][j-coins[i]])  
      

      second :同样在前面的代码中,min 的第二部分丢失了 + 1 所以正确的代码是

      if coins[i] > j
              dp[i][j] := min(dp[i-1][j], 1 + dp[i][j-coins[i]]) <-- HERE
      

      第三个:

      while j is not equal to 0
          if dp[i-1][j] is equal to min
              i := i - 1
          else
              Print(coins[i])
              j := j - coins[I] 
            // <---- HERE
          end if
      end while
      

      在这里,我们应该将 min 重新分配给新单元格,因此正确的代码是:

      while j is not equal to 0
          if dp[i-1][j] is equal to min
              i := i - 1
          else
              Print(coins[i])
              j := j - coins[I]
              min = dp[I][j]    
          end if
      end while
      

      非常感谢您的解决方案。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-02-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-04-25
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多