python换零钱_Python算法之零钱兑换问题的解法-程序员宅基地

技术标签: python换零钱  

26.jpg比如:顾客购物买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))

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_39611174/article/details/110241818

智能推荐

html表单常见操作汇总_html表单的处理程序有那些-程序员宅基地

文章浏览阅读159次。表单表单概述表单标签表单域按钮控件demo表单标签表单标签基本语法结构<form action="处理数据程序的url地址“ method=”get|post“ name="表单名称”></form><!--action,当提交表单时,向何处发送表单中的数据,地址可以是相对地址也可以是绝对地址--><!--method将表单中的数据传送给服务器处理,get方式直接显示在url地址中,数据可以被缓存,且长度有限制;而post方式数据隐藏传输,_html表单的处理程序有那些

PHP设置谷歌验证器(Google Authenticator)实现操作二步验证_php otp 验证器-程序员宅基地

文章浏览阅读1.2k次。使用说明:开启Google的登陆二步验证(即Google Authenticator服务)后用户登陆时需要输入额外由手机客户端生成的一次性密码。实现Google Authenticator功能需要服务器端和客户端的支持。服务器端负责密钥的生成、验证一次性密码是否正确。客户端记录密钥后生成一次性密码。下载谷歌验证类库文件放到项目合适位置(我这边放在项目Vender下面)https://github.com/PHPGangsta/GoogleAuthenticatorPHP代码示例://引入谷_php otp 验证器

【Python】matplotlib.plot画图横坐标混乱及间隔处理_matplotlib更改横轴间距-程序员宅基地

文章浏览阅读4.3k次,点赞5次,收藏11次。matplotlib.plot画图横坐标混乱及间隔处理_matplotlib更改横轴间距

docker — 容器存储_docker 保存容器-程序员宅基地

文章浏览阅读2.2k次。①Storage driver 处理各镜像层及容器层的处理细节,实现了多层数据的堆叠,为用户 提供了多层数据合并后的统一视图②所有 Storage driver 都使用可堆叠图像层和写时复制(CoW)策略③docker info 命令可查看当系统上的 storage driver主要用于测试目的,不建议用于生成环境。_docker 保存容器

OKHTTP3的依赖包与 权限_okhttp3 依赖包-程序员宅基地

文章浏览阅读2.1k次。依赖包: compile 'com.squareup.okio:okio:1.5.0' compile 'com.squareup.okhttp3:okhttp:3.2.0' compile 'com.squareup.okhttp3:logging-interceptor:3.4.1' compile 'com.google.code.gson:gson:2.8.2'_okhttp3 依赖包

Windows 10 桌面路径修改问题解决方案_无法更改桌面路径-程序员宅基地

文章浏览阅读150次。最近我遇到了一个问题,我在 Windows 10 上不小心修改了桌面路径,但现在无法将其改回原来的路径。我想知道如何通过编程来解决这个问题。要解决这个问题,我们可以使用编程来修改桌面路径。下面是一个使用 Python 编程语言的示例,演示了如何通过注册表修改桌面路径。在修改桌面路径后,你可能需要重新启动 Windows Explorer 进程以使更改生效。如果你有任何其他问题,请随时提问。注意:在运行以上代码之前,请确保你具有管理员权限。函数来将桌面路径修改为新路径。变量设置为你想要的新路径,并调用。_无法更改桌面路径

随便推点

网络拓扑结构_网络拓扑csdn-程序员宅基地

文章浏览阅读834次,点赞27次,收藏13次。网络拓扑结构是指计算机网络中各组件(如计算机、服务器、打印机、路由器、交换机等设备)及其连接线路在物理布局或逻辑构型上的排列形式。这种布局不仅描述了设备间的实际物理连接方式,也决定了数据在网络中流动的路径和方式。不同的网络拓扑结构影响着网络的性能、可靠性、可扩展性及管理维护的难易程度。_网络拓扑csdn

JS重写Date函数,兼容IOS系统_date.prototype 将所有 ios-程序员宅基地

文章浏览阅读1.8k次,点赞5次,收藏8次。IOS系统Date的坑要创建一个指定时间的new Date对象时,通常的做法是:new Date("2020-09-21 11:11:00")这行代码在 PC 端和安卓端都是正常的,而在 iOS 端则会提示 Invalid Date 无效日期。在IOS年月日中间的横岗许换成斜杠,也就是new Date("2020/09/21 11:11:00")通常为了兼容IOS的这个坑,需要做一些额外的特殊处理,笔者在开发的时候经常会忘了兼容IOS系统。所以就想试着重写Date函数,一劳永逸,避免每次ne_date.prototype 将所有 ios

如何将EXCEL表导入plsql数据库中-程序员宅基地

文章浏览阅读5.3k次。方法一:用PLSQL Developer工具。 1 在PLSQL Developer的sql window里输入select * from test for update; 2 按F8执行 3 打开锁, 再按一下加号. 鼠标点到第一列的列头,使全列成选中状态,然后粘贴,最后commit提交即可。(前提..._excel导入pl/sql

Git常用命令速查手册-程序员宅基地

文章浏览阅读83次。Git常用命令速查手册1、初始化仓库git init2、将文件添加到仓库git add 文件名 # 将工作区的某个文件添加到暂存区 git add -u # 添加所有被tracked文件中被修改或删除的文件信息到暂存区,不处理untracked的文件git add -A # 添加所有被tracked文件中被修改或删除的文件信息到暂存区,包括untracked的文件...

分享119个ASP.NET源码总有一个是你想要的_千博二手车源码v2023 build 1120-程序员宅基地

文章浏览阅读202次。分享119个ASP.NET源码总有一个是你想要的_千博二手车源码v2023 build 1120

【C++缺省函数】 空类默认产生的6个类成员函数_空类默认产生哪些类成员函数-程序员宅基地

文章浏览阅读1.8k次。版权声明:转载请注明出处 http://blog.csdn.net/irean_lau。目录(?)[+]1、缺省构造函数。2、缺省拷贝构造函数。3、 缺省析构函数。4、缺省赋值运算符。5、缺省取址运算符。6、 缺省取址运算符 const。[cpp] view plain copy_空类默认产生哪些类成员函数

推荐文章

热门文章

相关标签