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

一,问题描述

假设有 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] 表示不使用第 j 枚硬币, c[i, j-coinsValue[i]] 表示至少使用了一次 第 i 枚硬币。c[i, j-coinsValue[i]] 表示,第 i 枚硬币还可以继续使用。因为第一个参数还是 i

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

C() --> 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,故成功找零,此时 n=0了,这是一种找零方式 ==》 当n==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

时间: 2024-08-03 09:50:45

动态规划求解最多有几种方案求解硬币找零问题的相关文章

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

一,问题描述 给定一组硬币数,找出一组最少的硬币数,来找换零钱N. 比如,可用来找零的硬币为: 1.3.4   待找的钱数为 6.用两个面值为3的硬币找零,最少硬币数为2.而不是 4,1,1 因此,总结下该问题的特征:①硬币可重复多次使用.②在某些情况下,该问题可用贪心算法求解.具体可参考:某种 找换硬币问题的贪心算法的正确性证明 二,动态规划分析 为了更好的分析,先对该问题进行具体的定义:将用来找零的硬币的面值存储在一个数组中.如下: coinsValues[i] 表示第 i 枚硬币的面值.比

硬币找零问题之动态规划

今天我们看一下动态规划的硬币找零问题,主要通过一系列编程题分析动态规划的规律,只要掌握这一规律,许多动态规划的相关问题都可以类比得到. 题目1:给定数组arr,arr中所有的值都是正数且不重复.每个值代表一种面值的货币,每种面值的货币可以使用任意张,再给定一个整数aim代表要找的钱数,求组成aim的最少货币数. 举例: arr[5,2,3],aim=20.  4张5元可以组成20元,其他的找钱方案都要使用更多张的货币,所以返回4. 题解: 一眼看去这道题好像可以用贪心算法可解,但是仔细分析发现有

硬币找零-记忆化搜索(DP动态规划)

硬币找零 时间限制:1000 ms  |  内存限制:65535 KB 难度:3 描述 在现实生活中,我们经常遇到硬币找零的问题,例如,在发工资时,财务人员就需要计算最少的找零硬币数,以便他们能从银行拿回最少的硬币数,并保证能用这些硬币发工资. 我们应该注意到,人民币的硬币系统是 100,50,20,10,5,2,1,0.5,0.2,0.1,0.05, 0.02,0.01 元,采用这些硬币我们可以对任何一个工资数用贪心算法求出其最少硬币数. 但不幸的是: 我们可能没有这样一种好的硬币系统, 因此

动态规划-硬币找零

问题描述: 假设有几种硬币,如1,2,5,并且数量无限.请找出能够组成某个数目的找零所使用最少的硬币数. 问题分析: 用待找零的数值n,描述子结构/状态,记作sum[n],其值为所需的最小硬币数. 对于不同的硬币面值coin[0...T],有sum[k] = min0<=j<T{sum[k-coin[j]]}+1. 对应于给定数目的找零N,需要求解sum[N]的值. 类似于算法导论的钢条切割问题. def coinss(N): import sys opt=[sys.maxsize]*(N+1

硬币找零问题几种不同的实现方式(使用Python实现)

问题描述 ??假设你为一家自动售货机厂家编程序,自动售货机要每次找给顾客最少数量硬币:假设某次顾客投进$1纸币,买了?37的东西,要找?63,那么最少数量就是:2个quarter(?25).1个dime(?10)和3个penny(?1),一共6个. ??分别使用贪心算法,递归,以及递归的优化版本:递归 + 备忘录技术,与动态规划四种解法 ?? 问题抽象: coin_list = [1,10,25,100]. coin = 63. 在所给定coin_list等于coin最少个数.(63的最优组合是

动态规划 找零问题

动态规划的基本思想是将待求解问题分解成若干个子问题,先求解子问题,并将这些子问题的解保存起来,如果以后在求解较大子问题的时候需要用到这些子问题的解,就可以直接取出这些已经计算过的解而免去重复运算.保存子问题的解可以使用填表方式,例如保存在数组中. 用一个实际例子来体现动态规划的算法思想——硬币找零问题. 硬币找零问题描述:现存在一堆面值为 V1.V2.V3 … 个单位的硬币,问最少需要多少个硬币才能找出总值为 T 个单位的零钱?假设这一堆面值分别为 1.2.5.21.25 元,需要找出总值 T

三种方法求解约瑟夫环问题

约瑟夫环是一个数学的应用问题:已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围.从编号为k的人开始报数,数到m的那个人出列:他的下一个人又从1开始报数,数到m的那个人又出列:依此规律重复下去,直到圆桌周围的人全部出列. 方法1:使用stl::list模拟环形链表,参考剑指offer 代码: #include <iostream> #include <list> using namespace std; int lastNumber(unsigned int n,un

求解线性方程组的三种基本迭代法

前言 在实际项目的一些矩阵运算模块中,往往需要对线性方程组进行求解以得到最终结果. 然而,你无法让计算机去使用克莱默法则或者高斯消元法这样的纯数学方法来进行求解. 计算机解决这个问题的方法是迭代法.本文将介绍三种最为经典的迭代法并用经典C++源代码实现之. 迭代法简介 从解的某个近似值出发,通过构造一个无穷序列去逼近精确解的方法. 雅克比迭代法 计算流程: 1. 初始化系数矩阵等计算环境 2. 设置精度控制和迭代次数控制变量 3. 采用如下式子进行迭代计算: 4. 循环执行 3,若(条件a)当前

HBase存储时间相关多列数据的两种方案

所谓“时间相关多列数据”指的是同一个Key下具有多个属性数据,并且这些数据与时间具有相关性,多数场景是该Key对应在不同时间时刻的行为数据.在实际应用中,这类数据很多,例如电子商务网站上用户最近一段时间浏览的宝贝集合.访问的URL列表等. 使用HBase存储此类数据时,有以下两种常用的方案: 多行单列 表结构设计 Row Key:用户标识ID + (Long.MAX_VALUE - timestamp) Column Family:’cf’ Column Qualifier:’’ Value:宝