前言

关于找零钱问题,网上已经有很多相关的资料以及优秀的文章博客等。这里写这篇博客的初衷很简单,就是为了方便自己,回过头来捡起这个知识能快一点,接受起来更易理解点;他人的文章写的再好,毕竟是别人的,学习起来总有一定的困难。想法上,理解上总有一些不同的地方。所以在解决这个问题后,记录下我的笔记。

一、动态规划(DP)

1.1 概念

动态规划(dynamic programming)是运筹学的一个分支,是求解决策过程(decision process)最优化的数学方法

说明:上面是对该术语的简单解释,不是算法分析与设计中对动态规划的定义。本人对算法的认识比较浅显解释这个还是很困难的,而书本上概念性的知识我认为并不适合去学习理解。当然关于动态规划定义在1.5动态规划的理解中有详细介绍。

1.2 性质

动态规划一般用来处理最优解的问题。使用动态规划算法思想解决的问题一般具有最优子结构性质和重叠子问题这两个因素。

<1> 最优子结构

一个问题的最优解包含其子问题的最优解,这个性质被称为最优子结构性质

<2> 重叠子问题

递归算法求解问题时,每次产生的子问题并不总是新问题,有些子问题被反复计算多次。这种性质称为子问题的重叠性质。

1.3 基本步骤

<1> 找出最优解的性质,并刻划其结构特征。

<2> 递归地定义最优值。

<3> 以自底向上的方式计算出最优值。

<4> 根据计算最优值时得到的信息,构造最优解。

1.4 动态规划与分治法

动态规划和分治法有相似之处,都是将待解决问题分解为若干子问题。不同之处,分治法求解时有些子问题被重复计算了许多次;动态规划实现了存储这些子问题的解,以备子问题重复出现,当重叠子问题出现,找到已解决子问题的解即可,避免了大量的重复计算。

1.5 动态规划的理解(来源:知乎)

这里引用知乎王勐对动态规划的理解来作为动态规划的引入。

动态规划是对于 某一类问题 的解决方法!!重点在于如何鉴定“某一类问题”是动态规划可解的而不是纠结解决方法是递归还是递推!

当你企图使用计算机解决一个问题是,其实就是在思考如何将这个问题表达成状态(用哪些变量存储哪些数据)以及如何在状态中转移(怎样根据一些变量计算出另一些变量)。所以所谓的空间复杂度就是为了支持你的计算所必需存储的状态最多有多少,所谓时间复杂度就是从初始状态到达最终状态中间需要多少步!

一个问题是该用递推、贪心、搜索还是动态规划,完全是由这个问题本身阶段间状态的转移方式决定的!每个阶段只有一个状态->递推;

    每个阶段的最优状态都是由上一个阶段的最优状态得到的->贪心;

    每个阶段的最优状态是由之前所有阶段的状态的组合得到的->搜索;

    每个阶段的最优状态可以从之前某个阶段的某个或某些状态直接得到而不管之前这个状态是如何得到的->动态规划。

每个阶段的最优状态可以从之前某个阶段的某个或某些状态直接得到

    这个性质叫做最优子结构;

而不管之前这个状态是如何得到的

二、找零钱问题

2.1 问题描述

在现实生活中,经常遇到找零问题,假设有数目不限的面值为1元,5角,1角的硬币。给出需要找零金额,求出找零方案,要求:使用的硬币数目最少。

找零钱问题:

假设只有 1 分、 2 分、五分、 1 角、二角、 五角、 1 元的硬币。在超市结账时,如果需要找零钱,收银员希望将最少的硬币数找给顾客。那么,给定需要找的零钱数目,如何求得最少的硬币数呢?

2.2 问题分析

<1> 将硬币种类封装到数组 int coinsValues[]中,此处即

    int[] coinsValues = {1,2,5,10,20,50,100};

coinsValues[i] 表示第 i 枚硬币的面值为 coinsValues[i] ,单位:分。
coinsValues.length 表示硬币的种类

说明

为了使问题总有解,一般有一枚面值为 1 的硬币,此处已有。

<2> 设

    chargeOptimalSolution[coinKind][money]

表示可用第0、1、2...coinKind 种硬币对找零金额 money 找零

时所需要的最少硬币数。即当前问题的最优解。

  • coinKind 硬币种类,用来表示第几种硬币
  • money 当前找零总金额

<3> 对于某一种硬币来说,我们可以使用该种硬币进行找零,也可以不使用该种硬币进行找零。

于是根据当前逻辑分析,写出下面两个方法

    chargeOptimalSolution[coinKind - 1][money]chargeOptimalSolution[coinKind][money - coinsValues[coinKind - 1]] + 1
  • chargeOptimalSolution[coinKind - 1][money]

表示使用第 coinKind 种硬币的情况下找零所需的最少硬币数

  • chargeOptimalSolution[coinKind][money - coinsValues[coinKind]] + 1

表示不使用第 coinKind 种硬币的情况下找零所需的最少硬币数,因为使用了该种硬币一次,最少硬币数 chargeOptimalSolution[][] 加一。

<4> 最优解则为二者情况中较小的一个

    //使用第 i(coinkind) 种硬币时所需的最小硬币数-- 递推 --//不使用第 i(coinkind) 种硬币找零时需要的最小硬币数-- 递推 --int numberByCoinKind = chargeOptimalSolution[coinKind - 1][money];int numberNotByCoinKind = chargeOptimalSolution[coinKind][money - coinsValues[coinKind-1]] + 1;//逻辑判断硬币数目选其中较小的chargeOptimalSolution[coinKind][money] =numberByCoinKind < numberNotByCoinKind ? numberByCoinKind : numberNotByCoinKind;

<5> 特殊情况

以上业务逻辑,基本上就解决了这个问题。下面说说特殊情况。

  • (1)边界问题:

不存在对金额不为 0 的情况下找零的硬币种类为 0 ;这不就是摆明了不想找钱给顾客,黑店啊。

不存在对币种不为 0 的情况下对找零金额为 0 的进行找零;这不就是摆明了送钱吗,赔本生意。

对找零金额 money (money != 0),可用硬币种类为 0 种,不找钱了?

    chargeOptimalSolution[0][money]

对找零金额为 0,可用硬币coinKind (coinKind != 0),有钱任性,送钱了,顾客一脸茫然。

    chargeOptimalSolution[coinKind][0]
  • (2)可用币种面值大于找零金额的情况

收银员收费后需要找零 5 角,找了一张面值 50 元的面币,还干不干了这生意。

    coinsValues[coinKind] > money

<6>综上
    找零问题的解决点就在下面这个公式:

    最优解(最少硬币数) = min{chargeOptimalSolution[coinKind - 1][money], chargeOptimalSolution[coinKind][money - coinsValues[coinKind]] + 1}

min{a, b} 表示 a, b 中最小的那个数。

2.3 代码实现

ChargeProblem.Java

package common.test;import java.util.Arrays;
/*** * @since 2017-10-16* @author niaonao**/
public class ChargeProblem {/*** 通过面值为 coinsValues[i] 的硬币对金额 chargeMoney 找零* @param coinsValues 硬币面值coinsValues[i],硬币面值种类数量coinsValues.length* @param chargeMoney 找零金额* @return 最小找零硬币数目*/public static int charge(int[] coinsValues, int chargeMoney){int coinsKinds = coinsValues.length;int[][] chargeOptimalSolution = new int[coinsKinds + 1][chargeMoney + 1];//当找零金额为 0 时,不需要找零,最少找零硬币数量为 0for(int i = 0; i <= coinsKinds; i++)chargeOptimalSolution[i][0] = 0;//当找零金额不为 0 时,找零硬币种类不可为 0 for(int i = 0; i <= chargeMoney; i++)chargeOptimalSolution[0][i] = Integer.MAX_VALUE;//money 找零金额; coinKind 硬币种类,用来表示第几种硬币for(int money = 1; money <=chargeMoney; money++){for(int coinKind = 1; coinKind <= coinsKinds; coinKind++){//找零金额小于当前硬币面值if(money < coinsValues[coinKind-1]){chargeOptimalSolution[coinKind][money] = chargeOptimalSolution[coinKind - 1][money];continue;}//不使用第 i(coinkind) 种硬币找零时需要的最小硬币数-- 递推 --//使用第 i(coinkind) 种硬币时所需的最小硬币数-- 递推 --int numberByCoinKind = chargeOptimalSolution[coinKind - 1][money];int numberNotByCoinKind = chargeOptimalSolution[coinKind][money - coinsValues[coinKind-1]] + 1;//逻辑判断硬币数目选其中较小的chargeOptimalSolution[coinKind][money] = numberByCoinKind < numberNotByCoinKind ? numberByCoinKind : numberNotByCoinKind;}}return chargeOptimalSolution[coinsKinds][chargeMoney];}public static void main(String[] args) {//初始化硬币种类数组int[] coinsValues = {1,2,5,10,20,50,100};Arrays.sort(coinsValues);//初始化找零金额为625int chargeMoney = 625;int minCoinsNumber = charge(coinsValues, chargeMoney);System.out.println("给定找零金额" + chargeMoney+ ",收银员最少的找零硬币数为" + minCoinsNumber);}
}

2.4 运行结果

动态规划算法思想解决找零钱问题相关推荐

  1. Java使用动态规划算法思想解决01背包问题

    Java使用动态规划算法思想解决背包问题 背包问题是一种组合优化的NP完全问题.问题可以描述为:给定一组物品,每种物品都有自己的重量和价格,在限定的总重量内,我们如何选择,才能使得物品的总价格最高 动 ...

  2. 动态规划解决找零钱问题

    动态规划算法 通常用于求解具有某种最优性质的问题.动态规划算法与分治法类似,其基本思想都是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解.与分治法不同的是,适合于用动 ...

  3. 动态规划 --- 算法思想介绍

    一.动态规划的基本概念 动态规划在五种算法设计方法中难度最大,它建立在最优原则的基础上.采用动态规划方法,可以高效地解决许多用贪婪算法或分治法无法解决的问题.动态规划(dynamic programm ...

  4. 【算法分析与设计】实验 动态规划算法解决找零钱问题

    问题描述 设有 n n n种不同面值的硬币,各硬币的面值存于数组 T [ 1 : n ] T[1:n] T[1

  5. 贪心算法解决找零钱问题

    4.1 找零问题 问题描述: 设有50.20.10.5.1.0.5.0.1等面额的零钱,顾 客购物花了n元,在支付(n / 100 + 1) * 100元后,收银员应如何找 零,才能使找回的钱数最少. ...

  6. 用动态规划算法求解最少硬币问题 c语言,动态规划算法求解硬币找零问题

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 看着这代码怎么这么熟悉. package 动态规划找零; import java.util.Scanner; public class Main { pu ...

  7. 滑动窗口算法思想,找出字符串中的所有字母异位词

    力扣大神写的诗 一开始不理解滑动窗口是什么! 看完此诗我才明白,滑动窗口其实就是双指针的一种形式 左右指针齐头进. c#代码如下 public class Solution {public IList ...

  8. 在C语言中使用二分法算法思想解决猜商品价格问题

    电视抽奖活动中,根据主持人给出的提示(高了还是低了),观众在规定时间内猜中该商品价格即可获得该商品 所谓的二分查找法,其实是一种有序的查找方法,也称折半查找(Binary Search),如果是无序的 ...

  9. 动态规划算法入门---java版

    转载:http://blog.csdn.net/p10010/article/details/50196211 动态规划算法(后附常见动态规划为题及Java代码实现) 一.基本概念 动态规划过程是:每 ...

最新文章

  1. java创建对象_java 创建对象的五种方式
  2. PTA 基础编程题目集 7-14 求整数段和 C语言
  3. 【响应式Web前端设计】Viewport解析
  4. 执行srvctl报错 :error while loading shared libraries: libpthread.so.0:
  5. 关于ALTERA的FPGA的弱上拉问题
  6. 速战速决?你不会是不行吧......
  7. 深入深出Sigmoid与Softmax的血缘关系
  8. 分块试水--CODEVS4927 线段树练习5
  9. Python机器学习:PCA与梯度上升001什么是PCA
  10. 更新k8s镜像版本的三种方式
  11. jQuery使table表格隔行显示不同颜色
  12. 【疫情模型】基于matlab SEIR模型疫情分析预测【含Matlab源码 666期】
  13. C语言libxml用法,c语言libxml2库的安装和使用.....
  14. 3Q双向可控硅与4Q双向可控硅对比好处
  15. 告警关联中的频繁项集挖掘问题
  16. Homekit智能通断器
  17. 数据结构与算法——深入理解红-黑树!
  18. 梦想家-致停不下来的我们
  19. 跨境运营培训做亚马逊广告要注意什么
  20. matlab车牌识别的外文文献翻译,汽车车牌识别系统(带外文翻译).doc

热门文章

  1. vc6 调试 附加到进程 列表空_今天,进程告诉我线程它它它它不想活了
  2. 二叉树中的结点数目确定时,高度最小的一定是完全二叉树?
  3. 相机与IMU标定教程
  4. java毕业设计——基于java+Applet+access的综合测评系统设计与实现(毕业论文+程序源码)——综合测评系统
  5. (三)Horizon 队列管理工具
  6. C语言 数组的指针和指向数组的指针变量-2
  7. order by 空值排在最后_ZSBL高中组晋级赛圆满结束!汤溪中学杀出重围,顺利夺下最后一张总决赛门票!...
  8. 信息检索与利用(第三版)第六章 搜索引擎
  9. 电子商务基础:中小企业建站方案和资源
  10. MA模型自协方差证明