📜  用动态规划理解硬币找零问题

📅  最后修改于: 2021-09-22 10:12:20             🧑  作者: Mango

许多人认为硬币找零问题对于理解称为动态编程的编程范式至关重要。这两者通常总是配对在一起,因为硬币找零问题包含动态规划的概念。对于那些不了解动态规划的人来说,这是根据维基百科,

换句话说,动态问题是一种编程方法,用于将问题简化为更小的部分。例如,如果你被问到什么是 3 * 89?你可能不知道答案,因为你可能知道什么是 2 * 2。但是,如果你知道什么是 3 * 88 (264) 那么你当然可以推导出 3 * 89。你所要做的就是在前面的倍数上加上 3,你会得到 267 的答案。因此,这是对动态规划的一个非常简单的解释,也许你现在可以看到它如何有效地解决大的时间复杂度问题。
通过牢记上述动态规划的定义,我们现在可以继续解决硬币找零问题。以下是硬币找零问题的众多变体之一的示例。给定一个硬币列表,即1 美分、5 美分和 10 美分,你能确定给定列表中硬币的总组合数来组成数字N吗?
例 1 :假设给你 1 美分、5 美分和 10 美分的硬币,其中 N = 8 美分,你可以安排多少种硬币组合以获得 8 美分。

Input: N=8
        Coins : 1, 5, 10
Output: 2

Explanation: 
1 way: 
      1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 = 8 cents.
2 way:
      1 + 1 + 1 + 5 = 8 cents.

您所做的就是确定所有可以提出 8 美分面额的方法。八个 1 美分加在一起等于 8 美分。三个 1 美分加一个 5 美分加是 8 美分。因此,给定硬币 1、5 和 10 的列表,共有 2 种方法可以获得 8 美分。
例 2 :假设给你 1 美分、5 美分和 10 美分的硬币,其中 N = 10 美分,你可以安排多少种硬币组合以获得 10 美分。

Input : N=10
        Coins : 1, 5, 10
Output : 4
Explanation: 
1 way: 
   1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 = 10 cents.
2 way: 
   1 + 1 + 1 + 1 + 1 + 5 = 10 cents.
3 way: 
   5 + 5 = 10 cents.
4 way: 
   10 cents = 10 cents.

既然我们知道了问题陈述以及如何找到较小值的解决方案,我们将如何确定增加到较大值的硬币组合的总数?我们写一个程序。我们如何编写程序来计算获得更大 N 值的所有方法?简单我们使用动态规划。请记住,动态规划背后的想法是将问题的每个部分切成更小的部分。类似于页面顶部的示例。如果我们不知道 4 * 36 的值,但知道 4 * 35 (140) 的值,我们可以在该值上加上 4 并得到 4 * 36 的答案,顺便说一下,它是 144。
好的,我们明白我们必须做什么,但是程序如何确定硬币列表可以输出 N 的方式?好吧,让我们看看这个例子。

N = 12         
Index of Array: [0, 1,  2]
Array of coins: [1, 5, 10]

这是一组硬币,1 美分、5 美分和 10 美分。 N 是 12 美分。所以我们需要想出一种方法,可以使用这些硬币的价值并确定我们可以赚取 12 美分的方法数量。
动态思考,我们需要弄清楚如何添加到以前的数据。所以这意味着我们必须添加到以前的解决方案中,而不是重新计算相同的值。显然,我们必须遍历整个硬币数组。我们还需要一种方法来查看硬币是否大于 N 值。
一种方法是使用一个一直计数到第N 个值的数组。
所以 …
数组方式:

[0, 0, 0 ..... Nth value] in our case it would be up to 12.

将数组设置为第 N 个值的原因是,我们可以确定硬币构成Array of way索引处的值的方式数。我们这样做是因为如果我们可以确定一枚硬币大于该指数的价值,那么显然我们不能使用该硬币来确定硬币的组合,因为该硬币大于该价值。这可以通过一个例子更好地理解。
以上面的数字为例。

N = 12         
Index of Array of Coins:    
  [0, 1,  2]     
Array of coins:
  [1, 5, 10]

Index of Array of ways:   
  [0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10,  11,  12]
Array of  ways:            
  [0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,   0,    0]

在我们开始迭代之前,我们必须给我们的 way 数组一个预定义的值。我们必须将 way 数组索引 0 处的第一个元素设置为 1。这是因为有 1 种方法可以使用 0 个硬币使数字为 0。
因此,如果我们开始遍历所有硬币数组并将元素与方式数组进行比较,我们将确定硬币可用于生成方式数组索引处的值的次数。
例如…
首先设置 way[0] = 1。
让我们比较第一枚硬币,1 美分。

N = 12         
Index of Array of Coins:    
  [0, 1,  2]     
Array of coins:             
  [1, 5, 10]

Index of Array of ways:    
  [0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10,  11,  12]
Array of  ways:            
  [0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,   0,    0]

Then compare coins[0] to all of the index's 
of ways array. If the value of the coin is less 
than or equal to the ways index, then
ways[j-coins[i]]+ways[j] is the new value of 
ways[j]. We do this because we are 
trying to break each part down into smaller 
pieces. You will see what is happening as 
you continue to read. So comparing each value of the 
ways index to the first coin, we get the following.

Index of Array of ways:    
  [0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10,  11,  12]
Array of  ways:            
  [1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,   1,    1]

现在让我们比较第二个硬币,5 美分。

N = 12         
Index of Array of Coins:    
  [0, 1,  2]     
Array of coins:             
  [1, 5, 10]

Index of Array of ways:    
  [0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10,  11,  12]
Array of  ways:            
  [1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,   1,    1]

Comparing 5 cents to each of the index and
making that same comparison, if the value 
of the coin is smaller than the value of 
the index at the ways array then ways[j-coins[i]]+ways[j] 
is the new value of ways[j]. Thus we
get the following.

Index of Array of ways:    
  [0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10,  11,  12]
Array of  ways:            
  [1,  1,  1,  1,  1,  2,  2,  2,  2,  2,  3,   3,    3]

We are determining how many times the second
coin goes into all of the values leading up the
Nth coin. Why are we using all 
of the coins? It is to check our previous 
result dynamically and update our answer 
instead of recalculating all over again. 
For example take the element at index 10 
the answer is 3 so far. But how did we get 3? 
We know that the value of 10-5 is 5 so that
is our j-coins[i] value, that is the 
difference of what needs to be made up to 
make the amount 10. So we look at index 5 of the
ways array and see it has the value 2, for
the same reason as above, there are so far 2 
ways to obtain the value 5. So if there are 
2 ways to obtain the value 5 then those ways
plus the current number of ways is the new
updated value of the TOTAL 
ways to get the value at index 10.                                    

现在让我们比较第三个硬币,10 美分。

N = 12         
Index of Array of Coins:    
  [0, 1,  2]     
Array of coins:             
  [1, 5, 10]

Comparing 10 cents to each of the index
and making that same comparison, if the 
value of the coin is smaller than the value of the 
index at the ways array then 
ways[j-coins[i]]+ways[j] is the new value of ways[j]. 
Thus we get the following.


Index of Array of ways:    
  [0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10,  11,  12]
Array of  ways:            
  [1,  1,  1,  1,  1,  2,  2,  2,  2,  2,  4,   4,    4]

So the answer to our example is ways[12] which is 4.

考虑到上述所有内容,让我们看看下面的程序。

C++
#include 
using namespace std;
 
/* We have input values of N and an array Coins
  that holds all of the coins. We use data type
  of long because we want to be able to test
  large values
without integer overflow*/
long getNumberOfWays(long N, vector Coins)
{
     
    // Create the ways array to 1 plus the amount
    // to stop overflow
    vector ways(N + 1);
 
    // Set the first way to 1 because its 0 and
    // there is 1 way to make 0 with 0 coins
    ways[0] = 1;
 
     // Go through all of the coins
    for(int i = 0; i < Coins.size(); i++)
    {
         
        // Make a comparison to each index value
        // of ways with the coin value.
        for(int j = 0; j < ways.size(); j++)
        {
            if (Coins[i] <= j)
            {
                 
                // Update the ways array
                ways[j] += ways[(j - Coins[i])];
            }
        }
    }
 
    // Return the value at the Nth position
    // of the ways array.
    return ways[N];
}
 
void printArray(vector coins)
{
    for(long i : coins)
        cout << i << "\n";
}
 
// Driver Code
int main()
{
    vector Coins = { 1, 5, 10 };
     
    cout << "The Coins Array:" << endl;
    printArray(Coins);
     
    cout << "Solution:" << endl;
    cout << getNumberOfWays(12, Coins) << endl;
}
 
// This code is contributed by mohit kumar 29


Java
/* We have input values of N and an array Coins 
  that holds all of the coins. We use data type
  of long because we want to be able to test
  large values
without integer overflow*/
 
class getWays {
 
    static long getNumberOfWays(long N, long[] Coins)
    {
        // Create the ways array to 1 plus the amount
        // to stop overflow
        long[] ways = new long[(int)N + 1];
 
        // Set the first way to 1 because its 0 and
        // there is 1 way to make 0 with 0 coins
        ways[0] = 1;
 
         // Go through all of the coins
        for (int i = 0; i < Coins.length; i++) {
 
            // Make a comparison to each index value
            // of ways with the coin value.
            for (int j = 0; j < ways.length; j++) {
                if (Coins[i] <= j) {
      
                    // Update the ways array
                    ways[j] += ways[(int)(j - Coins[i])];
                }
            }
        }
 
        // return the value at the Nth position
        // of the ways array.   
        return ways[(int)N];
    }
 
    static void printArray(long[] coins)
    {
        for (long i : coins)
            System.out.println(i);
    }
 
    public static void main(String args[])
    {
        long Coins[] = { 1, 5, 10 };
 
        System.out.println("The Coins Array:");
        printArray(Coins);
 
        System.out.println("Solution:");
        System.out.println(getNumberOfWays(12, Coins));
    }
}


Python3
''' We have input values of N and an array Coins
that holds all of the coins. We use data type
of because long we want to be able to test
large values
without integer overflow'''
 
def getNumberOfWays(N, Coins):
 
    # Create the ways array to 1 plus the amount
    # to stop overflow
    ways = [0] * (N + 1);
 
    # Set the first way to 1 because its 0 and
    # there is 1 way to make 0 with 0 coins
    ways[0] = 1;
 
    # Go through all of the coins
    for i in range(len(Coins)):
 
        # Make a comparison to each index value
        # of ways with the coin value.
        for j in range(len(ways)):
            if (Coins[i] <= j):
 
                # Update the ways array
                ways[j] += ways[(int)(j - Coins[i])];
 
    # return the value at the Nth position
    # of the ways array.
    return ways[N];
 
def printArray(coins):
    for i in coins:
        print(i);
 
if __name__ == '__main__':
    Coins = [1, 5, 10];
 
    print("The Coins Array:");
    printArray(Coins);
 
    print("Solution:",end="");
    print(getNumberOfWays(12, Coins));
 
# This code is contributed by 29AjayKumar


C#
/* We have input values of N and
an array Coins that holds all of
the coins. We use data type of
long because we want to be able
to test large values without
integer overflow*/
using System;
 
public class getWays
{
 
    static long getNumberOfWays(long N, long[] Coins)
    {
        // Create the ways array to 1 plus the amount
        // to stop overflow
        long[] ways = new long[(int)N + 1];
 
        // Set the first way to 1 because its 0 and
        // there is 1 way to make 0 with 0 coins
        ways[0] = 1;
 
        // Go through all of the coins
        for (int i = 0; i < Coins.Length; i++)
        {
 
            // Make a comparison to each index value
            // of ways with the coin value.
            for (int j = 0; j < ways.Length; j++)
            {
                if (Coins[i] <= j)
                {
     
                    // Update the ways array
                    ways[j] += ways[(int)(j - Coins[i])];
                }
            }
        }
 
        // return the value at the Nth position
        // of the ways array.
        return ways[(int)N];
    }
 
    static void printArray(long[] coins)
    {
        foreach (long i in coins)
            Console.WriteLine(i);
    }
 
    // Driver code
    public static void Main(String []args)
    {
        long []Coins = { 1, 5, 10 };
 
        Console.WriteLine("The Coins Array:");
        printArray(Coins);
 
        Console.WriteLine("Solution:");
        Console.WriteLine(getNumberOfWays(12, Coins));
    }
}
 
// This code has been contributed by 29AjayKumar


Javascript


输出:
The Coins Array:
1
5
10
Solution:
4