比如:顾客购物买37元东西,给了100元,要找63元,那最少数量就是1张50元,1张10元,3张1元,一共4张。

方法一: 贪心策略

解决这个问题,最直观的就是使用贪心策略。我们会从最大面值的钱开始,用最多的数量。有余额再到下一个最大面值,还用最多的数量,一直到1元为止。

def chage_give(coins_list, change):

solutions = []

s_list = sorted(coins_list, reverse=True)

for coin in s_list:

coins_num = change // coin

solutions += [coin,] * coins_num

change = change - coin * coins_num

if coins_num < 0:

break

return len(solutions)

if __name__ == "__main__":

print(chage_give([1, 5, 10, 20, 50, 100], 63))

运行程序,输出打印结果:

>>> 5

贪心策略在人民币的体系下表现还好,但是如果当存在有21元的面值,贪心策略就会失效。因为63元的最优解是3个面值21元。

方法二:递归调用的方式求解

既然贪心策略在特殊的面值下会失效,那我们用递归解决这个问题吧。

递归的三个首先条件,我们先确定基本结束条件:剩余需要兑换的零钱正好等于某面值。例如找零10元,答案就是1张10元。

其次是缩小问题的规模:

找零减去 1元 后, 求兑换零钱的最少数量(递归调用自身);

找零减去 5元 后, 求兑换零钱的最少数量;

找零减去 10元后, 求兑换零钱的最少数量;

找零减去 20元后, 求兑换零钱的最少数量;

找零减去 50元后, 求兑换零钱的最少数量;

上述 5项 中选择最小的一个

def change_give_recursion(coins_list, change):

min_coins = change

# 当要兑换的零钱的值正好等于面值列表中其中一项,就直接返回 1

if change in coins_list:

return 1

else:

# 对各种面值都试用递归调用自身,但选择数量最小的一个。

for i in [ c for c in coins_list if c < change]:

coin_num = 1 + change_give_recursion(coins_list, change - i)

if coin_num < min_coins:

min_coins = coin_num

return min_coins

if __name__ == "__main__":

print(change_give_recursion([1, 5, 10, 20, 50, 100], 63))

运行程序,输出打印结果:

>>> 5

上面递归解法虽然能解决问题,但最大的问题是:非常低效!例如对63元的兑换问题需要进行6千多万的递归调用,有太多的重复计算。所以优化这个算法,我们需要消除重复的计算。

我们可以用一个表将计算过的中间结果保存起来,在计算之前查表看看是否已经计算过。这个算法的中间结果就是部分找零的最优解,在递归调用之前先查找表中是否已有部分找零的最优解,如果有,直接返回最优解而不进行递归调用,如果没有,才进行递归调用。

既然存在问题,我们就要做改进,改进后如下:

def change_give_recursion(coins_list, change, known_result):

min_coins = change

if change in coins_list:

# 如果兑换的零钱正好等于其中一个币值,就先将这个结果记录下来

known_result[change] = 1

return 1

elif known_result[change] > 0: # 如果已经存在这个零钱值的结果记录就直接返回

return known_result[change]

else:

for i in [ c for c in coins_list if c < change]:

coin_num = 1 + change_give_recursion(coins_list, change - i, known_result)

if coin_num < min_coins:

min_coins = coin_num

# 将得到的结果保存下来,后面调用是供之后检查

known_result[change] = min_coins

return min_coins

if __name__ == "__main__":

print(change_give_recursion([1, 5, 10, 20, 50, 100], 63, [0] * 64))

改进后的解法,极大的减少了递归调用次数。对63元的兑换问题仅需要进行221次的递归调用,是改进前的三十万分之一。这种中间结果记录的方法叫做“memoization”(记忆化/函数值缓冲)技术,提高了递归解法的性能,这种方法的应用如缓冲。

方法三:动态规划解法

动态规划(Dynamic programming,简称DP)主要用来解决一些希望找到问题最优解的优化问题

找零兑换的动态规划算法从最简单的“1元钱找零”的最优解开始,逐步递加上去,直到我们需要的找零数。在找零递加的过程中,设法保持每一分钱的递加都是最优解,一直加到求解找零数,自然得到最优解。

递加的过程能保持最优解的关键是,其依赖于更少钱数最优解的计算,而更少钱数的最优解已经得到了。问题的最优解包含了更小规模子问题的最优解,这是一个最优化问题能够用动态规划策略解决的必要条件。

计算11分钱的兑换法,我们做如下几步:

1、减去1分钱,剩下10分钱查表最优解是1

2、然后减去5分钱,剩下6分钱查表最优解是2

3、最后减去10分钱,剩下1分钱查表最优解是1

通过上述最小值得到最优解:2个硬币

def change_give_dynamic(coins_list, change, min_coins):

for cents in range(1, change + 1):

coins_count = cents

for j in [c for c in coins_list if c < cents]:

if min_coins[cents - j] + 1 < coins_count:

coins_count = min_coins[cents - j] + 1

min_coins[cents] = coins_count

return min_coins[change]

if __name__ == "__main__":

print(change_give_dynamic([1, 5, 10, 20, 50, 100], 63, [0] * 64))

python换零钱_Python算法之零钱兑换问题的解法相关推荐

  1. python找零钱_Python递归 - 找零钱

    特殊的方法-循环: #无法解决某些情况,例如存在21元的零钱 def fun(n): count = 0 while n > 25: n = n - 25 count = count + 1 w ...

  2. python换硬币_Python 硬币兑换问题

    硬币兑换问题: 给定总金额为A的一张纸币,现要兑换成面额分别为a1,a2,....,an的硬币,且希望所得到的硬币个数最少. # 动态规划思想 dp方程式如下 # dp[0] = 0 # dp[i] ...

  3. python换硬币_Python的硬币兑换动态编程实现记录,CoinChange,最少,组合,python,规划

    题目为给定不同面值的n种硬币,面值加起来等于一个特定的数m,求最少需要多少枚硬币实现. 这个问题如果使用暴力求解,需要穷举所有可以加起来等m的组合,时间复杂度为O(m^n). def coin_27_ ...

  4. python冒泡排序时间复杂度_Python算法中的时间复杂度问题

    在实现算法的时候,通常会从两方面考虑算法的复杂度,即时间复杂度和空间复杂度.顾名思义,时间复杂度用于度量算法的计算工作量,空间复杂度用于度量算法占用的内存空间. 本文将从时间复杂度的概念出发,结合实际 ...

  5. python range倒序_Python算法学习之堆和堆排序

    什么是堆? 堆是一种完全二叉树(请你回顾下上一章的概念),有最大堆和最小堆两种. 最大堆: 对于每个非叶子节点 V,V 的值都比它的两个孩子大,称为 最大堆特性(heap order property ...

  6. python分数计算器_python算法——方程计算器小工具

    python算法--方程计算器小工具 工具介绍 方程计算器小工具使用python开发,可实现三元以内一次.二次等方程的计算,包含基本计算器的功能.可用于老师.学生.家长等快速验证方程的求解,检查学生作 ...

  7. mac的python换字体_Python -mac-画图及设置坐标中文字体

    最近有点闲暇时间,趁时学习一下Python,搭建好Python环境,环境搭建就不多说,网上很多教程,我使用的anaconda.然后开始了coding之路 import numpy as np impo ...

  8. python换源_python更换国内源

    python安装之后默认是使用国外的源,使用pip下载模块网速不是很好,有时候下到一半还会断开连接,非常不方便,最好配置国内的源. Windows下: 在Windows配置源可参考下面两个博客: 在本 ...

  9. python快速排序函数_python算法-快速排序

    快速排序: 学习快速排序,要先复习下递归: 递归的2个条件: 1. 函数自己调用自己 2.有一个退出的条件 练习:基于递归下一个函数,计算n!并且求出当n等于10的值. n!=n * n-1*-..* ...

最新文章

  1. ubuntu fctix
  2. electron 剪贴板 截图_用electron开发了一个屏幕截图工具
  3. node.js浅入深出---之fs模块
  4. linux中js文件有乱码,解决node.js读取文件时中文乱码问题
  5. Failed to read artifact descriptor for xxx:jar:版本号
  6. 在Ubuntu/mint里安装VMware tools(虚拟机增强工具)
  7. 今天我解决的sql中文乱码问题
  8. linux启动系统不显示进度条设置方法
  9. 第五节:泛型(泛型类、接口、方法、委托、泛型约束、泛型缓存、逆变和协变)
  10. C语言——判断是该年的第几天
  11. 7-1 顶点的度 (15 分)
  12. html 无组件上传图片,无组件上传图片到数据库中,最完整解决方案
  13. r语言代码html,R语言的R Markdown创建html
  14. 解决body设置height:100%无效问题
  15. 二级c语言准考证,考试准考证打印
  16. VMware硬盘不足踩坑记录:扩容分区 + 删除.cache文件夹
  17. java连接打印机并进行打印
  18. 斯坦福编程方法学作业讲解3---Karel机器人三大定律(上)
  19. java jca_Java加密体系结构(JCA)参考指南
  20. 【论文排版术】学习笔记1

热门文章

  1. 瑞典如何成为欧洲硅谷
  2. Base64编码和其隐写术
  3. HDU 3277 Marriage Match III(并查集+二分+最大流)
  4. 【电源专题】SMPS电源的EMI来源和测量方法
  5. 【第二章】 C语言之牛客网刷题笔记 【点进来保证让知识充实你一整天】
  6. Connection must be valid and open.
  7. zemax 非序列模式 双折射晶体
  8. BEEF的使用(简单版)
  9. CubeMX C6T6有关IIC的bug
  10. Ubuntu16.4安装搜狗拼音输入法