📜  硬币变化 | DP-7

📅  最后修改于: 2021-09-22 09:58:23             🧑  作者: Mango

给定一个值 N,如果我们想用 N 美分找零,并且我们有无限供应每个 S = { S1, S2, .. , Sm} 值硬币,我们有多少种方法可以找零?硬币的顺序无关紧要。
例如,对于 N = 4 和 S = {1,2,3},有四种解:{1,1,1,1},{1,1,2},{2,2},{1, 3}。所以输出应该是 4。对于 N = 10 和 S = {2, 5, 3, 6},有五个解:{2,2,2,2,2}, {2,2,3,3}, {2,2,6}、{2,3,5} 和 {5,5}。所以输出应该是5。

1) 最优子结构
为了计算解的总数,我们可以将所有集合解分成两个集合。
1) 不包含第 m 个硬币(或 Sm)的解决方案。
2) 包含至少一个 Sm 的溶液。
设 count(S[], m, n) 为计算解个数的函数,则可以写成 count(S[], m-1, n) 和 count(S[], m, n-Sm)。
因此,该问题具有最优子结构性质,因为该问题可以使用子问题的解来解决。

2) 重叠子问题
以下是硬币找零问题的简单递归实现。实现只是简单地遵循上面提到的递归结构。

C++
// Recursive C++ program for
// coin change problem.
#include 
using namespace std;
 
// Returns the count of ways we can
// sum S[0...m-1] coins to get sum n
int count(int S[], int m, int n)
{
     
    // If n is 0 then there is 1 solution
    // (do not include any coin)
    if (n == 0)
        return 1;
     
    // If n is less than 0 then no
    // solution exists
    if (n < 0)
        return 0;
 
    // If there are no coins and n
    // is greater than 0, then no
    // solution exist
    if (m <= 0 && n >= 1)
        return 0;
 
    // count is sum of solutions (i)
    // including S[m-1] (ii) excluding S[m-1]
    return count(S, m - 1, n) +
           count(S, m, n - S[m - 1]);
}
 
// Driver code
int main()
{
    int i, j;
    int arr[] = { 1, 2, 3 };
    int m = sizeof(arr) / sizeof(arr[0]);
     
    cout << " " << count(arr, m, 4);
     
    return 0;
}
 
// This code is contributed by shivanisinghss2110


C
// Recursive C program for
// coin change problem.
#include
 
// Returns the count of ways we can
// sum S[0...m-1] coins to get sum n
int count( int S[], int m, int n )
{
    // If n is 0 then there is 1 solution
    // (do not include any coin)
    if (n == 0)
        return 1;
     
    // If n is less than 0 then no
    // solution exists
    if (n < 0)
        return 0;
 
    // If there are no coins and n
    // is greater than 0, then no
    // solution exist
    if (m <=0 && n >= 1)
        return 0;
 
    // count is sum of solutions (i)
    // including S[m-1] (ii) excluding S[m-1]
    return count( S, m - 1, n ) + count( S, m, n-S[m-1] );
}
 
// Driver program to test above function
int main()
{
    int i, j;
    int arr[] = {1, 2, 3};
    int m = sizeof(arr)/sizeof(arr[0]);
    printf("%d ", count(arr, m, 4));
    getchar();
    return 0;
}


Python3
# Recursive Python3 program for
# coin change problem.
 
# Returns the count of ways we can sum
# S[0...m-1] coins to get sum n
def count(S, m, n ):
 
    # If n is 0 then there is 1
    # solution (do not include any coin)
    if (n == 0):
        return 1
 
    # If n is less than 0 then no
    # solution exists
    if (n < 0):
        return 0;
 
    # If there are no coins and n
    # is greater than 0, then no
    # solution exist
    if (m <=0 and n >= 1):
        return 0
 
    # count is sum of solutions (i)
    # including S[m-1] (ii) excluding S[m-1]
    return count( S, m - 1, n ) + count( S, m, n-S[m-1] );
 
# Driver program to test above function
arr = [1, 2, 3]
m = len(arr)
print(count(arr, m, 4))
 
# This code is contributed by Smitha Dinesh Semwal


C#
// Recursive C# program for
// coin change problem.
using System;
 
class GFG
{
    // Returns the count of ways we can
    // sum S[0...m-1] coins to get sum n
    static int count( int []S, int m, int n )
    {
        // If n is 0 then there is 1 solution
        // (do not include any coin)
        if (n == 0)
            return 1;
         
        // If n is less than 0 then no
        // solution exists
        if (n < 0)
            return 0;
     
        // If there are no coins and n
        // is greater than 0, then no
        // solution exist
        if (m <=0 && n >= 1)
            return 0;
     
        // count is sum of solutions (i)
        // including S[m-1] (ii) excluding S[m-1]
        return count( S, m - 1, n ) +
            count( S, m, n - S[m - 1] );
    }
     
    // Driver program
    public static void Main()
    {
         
        int []arr = {1, 2, 3};
        int m = arr.Length;
        Console.Write( count(arr, m, 4));
         
         
    }
}
// This code is contributed by Sam007


PHP
= 1)
        return 0;
 
    // count is sum of solutions (i)
    // including S[m-1] (ii) excluding S[m-1]
    return coun($S, $m - 1,$n ) +
           coun($S, $m, $n - $S[$m - 1] );
}
 
    // Driver Code
    $arr = array(1, 2, 3);
    $m = count($arr);
    echo coun($arr, $m, 4);
     
// This code is contributed by Sam007
?>


Javascript


C++
// C++ program for coin change problem.
#include
 
using namespace std;
 
int count( int S[], int m, int n )
{
    int i, j, x, y;
 
    // We need n+1 rows as the table
    // is constructed in bottom up
    // manner using the base case 0
    // value case (n = 0)
    int table[n + 1][m];
     
    // Fill the enteries for 0
    // value case (n = 0)
    for (i = 0; i < m; i++)
        table[0][i] = 1;
 
    // Fill rest of the table entries
    // in bottom up manner
    for (i = 1; i < n + 1; i++)
    {
        for (j = 0; j < m; j++)
        {
            // Count of solutions including S[j]
            x = (i-S[j] >= 0) ? table[i - S[j]][j] : 0;
 
            // Count of solutions excluding S[j]
            y = (j >= 1) ? table[i][j - 1] : 0;
 
            // total count
            table[i][j] = x + y;
        }
    }
    return table[n][m - 1];
}
 
// Driver Code
int main()
{
    int arr[] = {1, 2, 3};
    int m = sizeof(arr)/sizeof(arr[0]);
    int n = 4;
    cout << count(arr, m, n);
    return 0;
}
 
// This code is contributed
// by Akanksha Rai(Abby_akku)


C
// C program for coin change problem.
#include
 
int count( int S[], int m, int n )
{
    int i, j, x, y;
 
    // We need n+1 rows as the table is constructed
    // in bottom up manner using the base case 0
    // value case (n = 0)
    int table[n+1][m];
    
    // Fill the enteries for 0 value case (n = 0)
    for (i=0; i= 0)? table[i - S[j]][j]: 0;
 
            // Count of solutions excluding S[j]
            y = (j >= 1)? table[i][j-1]: 0;
 
            // total count
            table[i][j] = x + y;
        }
    }
    return table[n][m-1];
}
 
// Driver program to test above function
int main()
{
    int arr[] = {1, 2, 3};
    int m = sizeof(arr)/sizeof(arr[0]);
    int n = 4;
    printf(" %d ", count(arr, m, n));
    return 0;
}


Java
/* Dynamic Programming Java implementation of Coin
   Change problem */
import java.util.Arrays;
 
class CoinChange
{
    static long countWays(int S[], int m, int n)
    {
        //Time complexity of this function: O(mn)
        //Space Complexity of this function: O(n)
 
        // table[i] will be storing the number of solutions
        // for value i. We need n+1 rows as the table is
        // constructed in bottom up manner using the base
        // case (n = 0)
        long[] table = new long[n+1];
 
        // Initialize all table values as 0
        Arrays.fill(table, 0);   //O(n)
 
        // Base case (If given value is 0)
        table[0] = 1;
 
        // Pick all coins one by one and update the table[]
        // values after the index greater than or equal to
        // the value of the picked coin
        for (int i=0; i


Python
# Dynamic Programming Python implementation of Coin
# Change problem
def count(S, m, n):
    # We need n+1 rows as the table is constructed
    # in bottom up manner using the base case 0 value
    # case (n = 0)
    table = [[0 for x in range(m)] for x in range(n+1)]
 
    # Fill the entries for 0 value case (n = 0)
    for i in range(m):
        table[0][i] = 1
 
    # Fill rest of the table entries in bottom up manner
    for i in range(1, n+1):
        for j in range(m):
 
            # Count of solutions including S[j]
            x = table[i - S[j]][j] if i-S[j] >= 0 else 0
 
            # Count of solutions excluding S[j]
            y = table[i][j-1] if j >= 1 else 0
 
            # total count
            table[i][j] = x + y
 
    return table[n][m-1]
 
# Driver program to test above function
arr = [1, 2, 3]
m = len(arr)
n = 4
print(count(arr, m, n))
 
# This code is contributed by Bhavya Jain


C#
/* Dynamic Programming C# implementation of Coin
Change problem */
using System;
 
class GFG
{
    static long countWays(int []S, int m, int n)
    {
        //Time complexity of this function: O(mn)
        //Space Complexity of this function: O(n)
 
        // table[i] will be storing the number of solutions
        // for value i. We need n+1 rows as the table is
        // constructed in bottom up manner using the base
        // case (n = 0)
        int[] table = new int[n+1];
 
        // Initialize all table values as 0
        for(int i = 0; i < table.Length; i++)
        {
            table[i] = 0;
        }
 
        // Base case (If given value is 0)
        table[0] = 1;
 
        // Pick all coins one by one and update the table[]
        // values after the index greater than or equal to
        // the value of the picked coin
        for (int i = 0; i < m; i++)
            for (int j = S[i]; j <= n; j++)
                table[j] += table[j - S[i]];
 
        return table[n];
    }
 
    // Driver Function
    public static void Main()
    {
        int []arr = {1, 2, 3};
        int m = arr.Length;
        int n = 4;
        Console.Write(countWays(arr, m, n));
    }
}
// This code is contributed by Sam007


PHP
= 0) ?
                  $table[$i - $S[$j]][$j] : 0;
 
            // Count of solutions
            // excluding S[j]
            $y = ($j >= 1) ?
                  $table[$i][$j - 1] : 0;
 
            // total count
            $table[$i][$j] = $x + $y;
        }
    }
    return $table[$n][$m-1];
}
 
// Driver Code
$arr = array(1, 2, 3);
$m = count($arr);
$n = 4;
echo count1($arr, $m, $n);
 
// This code is contributed by mits
?>


Javascript


C++
int count( int S[], int m, int n )         
{         
 // table[i] will be storing the number of solutions for         
 // value i. We need n+1 rows as the table is constructed         
 // in bottom up manner using the base case (n = 0)         
 int table[n+1];         
 // Initialize all table values as 0         
 memset(table, 0, sizeof(table));         
 // Base case (If given value is 0)         
 table[0] = 1;         
 // Pick all coins one by one and update the table[] values         
 // after the index greater than or equal to the value of the         
 // picked coin         
 for(int i=0; i


Java
public static int count( int S[], int m, int n )
{
    // table[i] will be storing the number of solutions for
    // value i. We need n+1 rows as the table is constructed
    // in bottom up manner using the base case (n = 0)
    int table[]=new int[n+1];
 
    // Base case (If given value is 0)
    table[0] = 1;
 
    // Pick all coins one by one and update the table[] values
    // after the index greater than or equal to the value of the
    // picked coin
    for(int i=0; i


Python
# Dynamic Programming Python implementation of Coin
# Change problem
def count(S, m, n):
 
    # table[i] will be storing the number of solutions for
    # value i. We need n+1 rows as the table is constructed
    # in bottom up manner using the base case (n = 0)
    # Initialize all table values as 0
    table = [0 for k in range(n+1)]
 
    # Base case (If given value is 0)
    table[0] = 1
 
    # Pick all coins one by one and update the table[] values
    # after the index greater than or equal to the value of the
    # picked coin
    for i in range(0,m):
        for j in range(S[i],n+1):
            table[j] += table[j-S[i]]
 
    return table[n]
 
# Driver program to test above function
arr = [1, 2, 3]
m = len(arr)
n = 4
x = count(arr, m, n)
print (x)
 
# This code is contributed by Afzal Ansari


C#
// Dynamic Programming C# implementation
// of Coin Change problem
using System;
 
class GFG
{
static int count(int []S, int m, int n)
{
    // table[i] will be storing the
    // number of solutions for value i.
    // We need n+1 rows as the table
    // is constructed in bottom up manner
    // using the base case (n = 0)
    int [] table = new int[n + 1];
     
    // Base case (If given value is 0)
    table[0] = 1;
 
    // Pick all coins one by one and
    // update the table[] values after
    // the index greater than or equal
    // to the value of the picked coin
    for(int i = 0; i < m; i++)
        for(int j = S[i]; j <= n; j++)
            table[j] += table[j - S[i]];
 
    return table[n];
}
 
// Driver Code
public static void Main()
{
    int []arr = {1, 2, 3};
    int m = arr.Length;
    int n = 4;
    Console.Write(count(arr, m, n));
}
}
 
// This code is contributed by Raj


PHP


Javascript


C++
#include 
using namespace std;
 
int coinchange(vector& a, int v, int n,
               vector >& dp)
{
    if (v == 0)
        return dp[n][v] = 1;
    if (n == 0)
        return 0;
    if (dp[n][v] != -1)
        return dp[n][v];
    if (a[n - 1] <= v) {
        // Either Pick this coin or not
        return dp[n][v] = coinchange(a, v - a[n - 1], n, dp)
                          + coinchange(a, v, n - 1, dp);
    }
    else // We have no option but to leave this coin
        return dp[n][v] = coinchange(a, v, n - 1, dp);
}
int32_t main()
{
    int tc = 1;
    // cin >> tc;
    while (tc--) {
        int n, v;
        n = 3, v = 4;
        vector a = { 1, 2, 3 };
        vector > dp(n + 1,
                                vector(v + 1, -1));
        int res = coinchange(a, v, n, dp);
        cout << res << endl;
    }
}
// This Code is Contributed by
// Harshit Agrawal NITT


输出
4

需要注意的是,上述函数一次又一次地计算相同的子问题。对于 S = {1, 2, 3} 和 n = 5,请参见以下递归树。

函数C({1}, 3) 被调用了两次。如果我们绘制完整的树,那么我们可以看到有很多子问题被多次调用。

C() --> count()
                             C({1,2,3}, 5)                     
                           /             \    
                         /                 \                  
             C({1,2,3}, 2)                 C({1,2}, 5)
            /       \                      /      \         
           /         \                    /         \   
C({1,2,3}, -1)  C({1,2}, 2)        C({1,2}, 3)    C({1}, 5)
               /    \             /     \           /     \
             /       \           /       \         /        \
    C({1,2},0)  C({1},2)   C({1,2},1) C({1},3)    C({1}, 4)  C({}, 5)
                   / \     / \        /\         /     \         
                  /   \   /   \     /   \       /       \  
                .      .  .     .   .     .   C({1}, 3) C({}, 4)
                                               / \ 
                                              /   \   
                                             .      .

由于再次调用相同的子问题,因此该问题具有重叠子问题的属性。因此,硬币找零问题具有动态规划问题的两个性质(见this 和this)。与其他典型的动态规划 (DP) 问题一样,通过以自下而上的方式构造临时数组 table[][] 可以避免相同子问题的重新计算。

动态规划解决方案

C++

// C++ program for coin change problem.
#include
 
using namespace std;
 
int count( int S[], int m, int n )
{
    int i, j, x, y;
 
    // We need n+1 rows as the table
    // is constructed in bottom up
    // manner using the base case 0
    // value case (n = 0)
    int table[n + 1][m];
     
    // Fill the enteries for 0
    // value case (n = 0)
    for (i = 0; i < m; i++)
        table[0][i] = 1;
 
    // Fill rest of the table entries
    // in bottom up manner
    for (i = 1; i < n + 1; i++)
    {
        for (j = 0; j < m; j++)
        {
            // Count of solutions including S[j]
            x = (i-S[j] >= 0) ? table[i - S[j]][j] : 0;
 
            // Count of solutions excluding S[j]
            y = (j >= 1) ? table[i][j - 1] : 0;
 
            // total count
            table[i][j] = x + y;
        }
    }
    return table[n][m - 1];
}
 
// Driver Code
int main()
{
    int arr[] = {1, 2, 3};
    int m = sizeof(arr)/sizeof(arr[0]);
    int n = 4;
    cout << count(arr, m, n);
    return 0;
}
 
// This code is contributed
// by Akanksha Rai(Abby_akku)

C

// C program for coin change problem.
#include
 
int count( int S[], int m, int n )
{
    int i, j, x, y;
 
    // We need n+1 rows as the table is constructed
    // in bottom up manner using the base case 0
    // value case (n = 0)
    int table[n+1][m];
    
    // Fill the enteries for 0 value case (n = 0)
    for (i=0; i= 0)? table[i - S[j]][j]: 0;
 
            // Count of solutions excluding S[j]
            y = (j >= 1)? table[i][j-1]: 0;
 
            // total count
            table[i][j] = x + y;
        }
    }
    return table[n][m-1];
}
 
// Driver program to test above function
int main()
{
    int arr[] = {1, 2, 3};
    int m = sizeof(arr)/sizeof(arr[0]);
    int n = 4;
    printf(" %d ", count(arr, m, n));
    return 0;
}

Java

/* Dynamic Programming Java implementation of Coin
   Change problem */
import java.util.Arrays;
 
class CoinChange
{
    static long countWays(int S[], int m, int n)
    {
        //Time complexity of this function: O(mn)
        //Space Complexity of this function: O(n)
 
        // table[i] will be storing the number of solutions
        // for value i. We need n+1 rows as the table is
        // constructed in bottom up manner using the base
        // case (n = 0)
        long[] table = new long[n+1];
 
        // Initialize all table values as 0
        Arrays.fill(table, 0);   //O(n)
 
        // Base case (If given value is 0)
        table[0] = 1;
 
        // Pick all coins one by one and update the table[]
        // values after the index greater than or equal to
        // the value of the picked coin
        for (int i=0; i

Python

# Dynamic Programming Python implementation of Coin
# Change problem
def count(S, m, n):
    # We need n+1 rows as the table is constructed
    # in bottom up manner using the base case 0 value
    # case (n = 0)
    table = [[0 for x in range(m)] for x in range(n+1)]
 
    # Fill the entries for 0 value case (n = 0)
    for i in range(m):
        table[0][i] = 1
 
    # Fill rest of the table entries in bottom up manner
    for i in range(1, n+1):
        for j in range(m):
 
            # Count of solutions including S[j]
            x = table[i - S[j]][j] if i-S[j] >= 0 else 0
 
            # Count of solutions excluding S[j]
            y = table[i][j-1] if j >= 1 else 0
 
            # total count
            table[i][j] = x + y
 
    return table[n][m-1]
 
# Driver program to test above function
arr = [1, 2, 3]
m = len(arr)
n = 4
print(count(arr, m, n))
 
# This code is contributed by Bhavya Jain

C#

/* Dynamic Programming C# implementation of Coin
Change problem */
using System;
 
class GFG
{
    static long countWays(int []S, int m, int n)
    {
        //Time complexity of this function: O(mn)
        //Space Complexity of this function: O(n)
 
        // table[i] will be storing the number of solutions
        // for value i. We need n+1 rows as the table is
        // constructed in bottom up manner using the base
        // case (n = 0)
        int[] table = new int[n+1];
 
        // Initialize all table values as 0
        for(int i = 0; i < table.Length; i++)
        {
            table[i] = 0;
        }
 
        // Base case (If given value is 0)
        table[0] = 1;
 
        // Pick all coins one by one and update the table[]
        // values after the index greater than or equal to
        // the value of the picked coin
        for (int i = 0; i < m; i++)
            for (int j = S[i]; j <= n; j++)
                table[j] += table[j - S[i]];
 
        return table[n];
    }
 
    // Driver Function
    public static void Main()
    {
        int []arr = {1, 2, 3};
        int m = arr.Length;
        int n = 4;
        Console.Write(countWays(arr, m, n));
    }
}
// This code is contributed by Sam007

PHP

= 0) ?
                  $table[$i - $S[$j]][$j] : 0;
 
            // Count of solutions
            // excluding S[j]
            $y = ($j >= 1) ?
                  $table[$i][$j - 1] : 0;
 
            // total count
            $table[$i][$j] = $x + $y;
        }
    }
    return $table[$n][$m-1];
}
 
// Driver Code
$arr = array(1, 2, 3);
$m = count($arr);
$n = 4;
echo count1($arr, $m, $n);
 
// This code is contributed by mits
?>

Javascript


输出
4

时间复杂度: O(mn)
下面是方法2的简化版,这里需要的辅助空间只有O(n)。

C++

int count( int S[], int m, int n )         
{         
 // table[i] will be storing the number of solutions for         
 // value i. We need n+1 rows as the table is constructed         
 // in bottom up manner using the base case (n = 0)         
 int table[n+1];         
 // Initialize all table values as 0         
 memset(table, 0, sizeof(table));         
 // Base case (If given value is 0)         
 table[0] = 1;         
 // Pick all coins one by one and update the table[] values         
 // after the index greater than or equal to the value of the         
 // picked coin         
 for(int i=0; i

Java

public static int count( int S[], int m, int n )
{
    // table[i] will be storing the number of solutions for
    // value i. We need n+1 rows as the table is constructed
    // in bottom up manner using the base case (n = 0)
    int table[]=new int[n+1];
 
    // Base case (If given value is 0)
    table[0] = 1;
 
    // Pick all coins one by one and update the table[] values
    // after the index greater than or equal to the value of the
    // picked coin
    for(int i=0; i

Python

# Dynamic Programming Python implementation of Coin
# Change problem
def count(S, m, n):
 
    # table[i] will be storing the number of solutions for
    # value i. We need n+1 rows as the table is constructed
    # in bottom up manner using the base case (n = 0)
    # Initialize all table values as 0
    table = [0 for k in range(n+1)]
 
    # Base case (If given value is 0)
    table[0] = 1
 
    # Pick all coins one by one and update the table[] values
    # after the index greater than or equal to the value of the
    # picked coin
    for i in range(0,m):
        for j in range(S[i],n+1):
            table[j] += table[j-S[i]]
 
    return table[n]
 
# Driver program to test above function
arr = [1, 2, 3]
m = len(arr)
n = 4
x = count(arr, m, n)
print (x)
 
# This code is contributed by Afzal Ansari

C#

// Dynamic Programming C# implementation
// of Coin Change problem
using System;
 
class GFG
{
static int count(int []S, int m, int n)
{
    // table[i] will be storing the
    // number of solutions for value i.
    // We need n+1 rows as the table
    // is constructed in bottom up manner
    // using the base case (n = 0)
    int [] table = new int[n + 1];
     
    // Base case (If given value is 0)
    table[0] = 1;
 
    // Pick all coins one by one and
    // update the table[] values after
    // the index greater than or equal
    // to the value of the picked coin
    for(int i = 0; i < m; i++)
        for(int j = S[i]; j <= n; j++)
            table[j] += table[j - S[i]];
 
    return table[n];
}
 
// Driver Code
public static void Main()
{
    int []arr = {1, 2, 3};
    int m = arr.Length;
    int n = 4;
    Console.Write(count(arr, m, n));
}
}
 
// This code is contributed by Raj

PHP


Javascript


输出:

4

参考:
http://www.algorithmist.com/index。 PHP/Coin_Change

以下是另一种使用记忆化的自上而下 DP 方法:

C++

#include 
using namespace std;
 
int coinchange(vector& a, int v, int n,
               vector >& dp)
{
    if (v == 0)
        return dp[n][v] = 1;
    if (n == 0)
        return 0;
    if (dp[n][v] != -1)
        return dp[n][v];
    if (a[n - 1] <= v) {
        // Either Pick this coin or not
        return dp[n][v] = coinchange(a, v - a[n - 1], n, dp)
                          + coinchange(a, v, n - 1, dp);
    }
    else // We have no option but to leave this coin
        return dp[n][v] = coinchange(a, v, n - 1, dp);
}
int32_t main()
{
    int tc = 1;
    // cin >> tc;
    while (tc--) {
        int n, v;
        n = 3, v = 4;
        vector a = { 1, 2, 3 };
        vector > dp(n + 1,
                                vector(v + 1, -1));
        int res = coinchange(a, v, n, dp);
        cout << res << endl;
    }
}
// This Code is Contributed by
// Harshit Agrawal NITT
输出
4

时间复杂度: O(M*N)
辅助空间: O(M*N)

贡献者:Mayukh Sinha

如果您希望与专家一起参加现场课程,请参阅DSA 现场工作专业课程学生竞争性编程现场课程