给定金额和面额的最小硬币数量

时间:2016-11-25 12:00:24

标签: dynamic-programming coin-change

鉴于一组面额和所需金额,我必须找到赚取该金额的最小硬币数量,也是每种面额的硬币数量

请帮忙!!

2 个答案:

答案 0 :(得分:3)

确定获得该总和所需的最小硬币数量的伪代码是:

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 所需的最小硬币数量。我们还假设有无限量的硬币供应。我们将使用动态编程来解决这个问题。

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

首先,对于 0 列,可以通过不取任何硬币来使 0 。因此, 0 列的所有值都将为 0 。对于 dp [0] [1] ,我们问自己是否只有 1 硬币面额,即币[0] = 1 ,获得 1 所需的最低硬币数量是多少?答案是 1 。对于 dp [0] [2] ,如果我们只有 1 ,那么获得 2 所需的最小硬币数是多少。答案是 2 。同样 dp [0] [3] = 3 dp [0] [4] = 4 等等上。这里要提一点的是,如果我们没有一个面额硬币 1 ,可能会出现一些情况,使用 1 <无法实现总计/ strong>仅限硬币。为简单起见,我们在示例中采用 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] ,我们问自己是否有硬币[0] = 1 币[1] = 5 ,获得 1 所需的最低硬币数量是多少?由于币[1] 大于我们当前的总数,因此不会影响我们的计算。我们需要排除币[5] 并仅使用币[0] 获取 1 。该值存储在 dp [0] [1] 中。所以我们从顶部获取价值。我们的第一个公式是:

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

dp [1] [5] 中我们的总数 5 之前,这种情况才会成立,对于这种情况,我们可以 5 有两种方式:   - 我们采用 5 硬币[0] ,存储在 dp [0] [5] (从顶部)。   - 我们采用 1 硬币[1] 的面额和( 5 - 5 )= 0 < / strong> 硬币的面额[0]

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

对于 dp [1] [6] ,我们可以通过两种方式使 6 :   - 我们采用 6 币[0] 面额,存储在顶部。   - 我们可以采用 1 5 的面额,我们需要 6 - 5 = 1 得到总数。使用面额 1 5 的硬币获得 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

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

Direction Array

6 5

答案 1 :(得分:0)

代码中有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

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

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

非常感谢您的解决方案。