动态规划求解最多有几种方案求解硬币找零问题

简介:

一,问题描述

假设有 m 种面值不同的硬币,存储在 coinsValues数组中,现需要使用这些硬币来找钱,各种硬币的使用个数不限。 求对于给定的钱数N,我们最多有几种不同的找钱方式。硬币的顺序并不重要。

 

二,动态规划分析

为了更好的分析,先对该问题进行具体的定义:将用来找零的硬币的面值存储在一个数组中。如下:

coinsValues[i] 表示第 i 枚硬币的面值。比如,

第 i 枚硬币     面值

    1                1

    2                3

    3                4

待找零的钱数为 n (上面示例中 n=6)

为了使问题总有解,一般第1枚硬币的面值为1

设 c[i,j]表示 使用 第 1,2,...i 种面值的硬币时,需要找金额为 j 的钱,最多可采用多少种不同的方式?

i 表示可用的硬币种类数, j 表示 需要找回的零钱

①最优子结构

对于某种面值的硬币,要么使用了(可能使用多次)它,要么不使用它。故:

c[i,j]=c[i-1,j] + c[i,j-coinsValue[i]]

c[i-1,j] 表示不使用第 i 枚硬币, c[i, j-coinsValue[i]] 表示至少使用了一次 第 i 枚硬币。c[i, j-coinsValue[i]] 表示,第 i 枚硬币还可以继续使用。因为第一个参数还是 i

从这里可以看出:用到了《组合数学》中的加法原理。

 

如何确定初始(基准)条件?一个重要的方法就是画一个简单的实例图。(借用网上一张图:)

复制代码
C({1,2,3},j) --> recursiveChargeTypes
                              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)
                                               /  \
                                              /    \  
                                             .      .
复制代码

比如,按照红色那条路走,就知道 5 使用了硬币面值3 和 2,故成功找零,此时 j=0了,这是一种找零方式 ==》 当j==0时,返回1

 

三,代码实现

复制代码
public class DPCoinCharge {
    
    public static int chargeTypes(int[] coinsValues, int n){
        int m = coinsValues.length;
        int[][] c = new int[m+1][n+1];
        
        //基准条件,可参考下面的递归代码
        for(int i = 0; i <=m; i++)
            c[i][0] = 1;
        for(int i = 1; i <=n; i++)
            c[0][i] = 0;
        
        
        for(int i = 1; i <=m; i++)
        {
            for(int j = 1; j <=n; j++)
            {
                if(j < coinsValues[i-1])//第 i 枚硬币 不可用. (需要找 5块钱,但是现在只有一张百元大钞)
                {
                    c[i][j] = c[i-1][j];
                    continue;
                }
                //在第 i 枚硬币可用的情况下, 不使用 第 i 枚硬币 或者第 i 枚硬币至少使用一次---状态方程
                c[i][j] = c[i-1][j] + c[i][j - coinsValues[i-1]];//coinsValues下标从0开始
            }
        }
        return c[m][n];
    }
    
    //递归实现
    public static int recursiveChargeTypes(int[] coinsValues, int m, int n)
    {
        //基准条件 可以 通过画一个简单的实例 分析来得出. 比如 recursiveChargeTypes({1,3,4}, 3, 5)
        if(n == 0)
            return 1;
        if(n < 0)
            return 0;
        if(m <= 0)
            return 0;
        else
            return recursiveChargeTypes(coinsValues, m-1, n) + recursiveChargeTypes(coinsValues, m, n-coinsValues[m]);
    }
    
    public static void main(String[] args) {
        int[] coinsValues = {1,2,3};
        int n = 5;
        int maxTypes = chargeTypes(coinsValues, n);
        System.out.println(maxTypes);
    }
}
复制代码

 

四,参考资料

硬币找零问题的动态规划实现

某种 找换硬币问题的贪心算法的正确性证明

从 活动选择问题 看动态规划和贪心算法的区别与联系

http://www.acmerblog.com/dp6-coin-change-4973.html

本文转自hapjin博客园博客,原文链接:http://www.cnblogs.com/hapjin/p/5579737.html,如需转载请自行联系原作者

相关文章
动态规划|【斐波那契数列模型 】|746.使用最小花费爬楼梯
动态规划|【斐波那契数列模型 】|746.使用最小花费爬楼梯
|
10月前
|
算法 Java Python
深入理解动态规划算法 | 凑硬币
深入理解动态规划算法 | 凑硬币
82 0
|
10月前
蓝桥杯:暴力求解四平方和
蓝桥杯:暴力求解四平方和
38 0
|
10月前
|
算法 Java
动态规划算法-凑硬币
动态规划算法-凑硬币
85 0
|
缓存 算法
钞票找零-贪心,动态规划算法
钞票找零-贪心,动态规划算法
80 1
|
算法
【贪心法】分数背包问题
【贪心法】分数背包问题
194 0
【动态规划法】0-1背包问题
【动态规划法】0-1背包问题
118 0
【动态规划法】0-1背包问题
|
算法
【动态规划法】硬币找零问题
【动态规划法】硬币找零问题
266 0
【动态规划法】硬币找零问题
|
机器学习/深度学习
【组合数学】递推方程 ( 常系数线性非齐次递推方程求解 | 递推方程标准型及通解 | 递推方程通解证明 )
【组合数学】递推方程 ( 常系数线性非齐次递推方程求解 | 递推方程标准型及通解 | 递推方程通解证明 )
165 0
【组合数学】递推方程 ( 有重根递推方程求解问题 | 问题提出 )
【组合数学】递推方程 ( 有重根递推方程求解问题 | 问题提出 )
151 0