动态规划Dynamic programming详解-硬币找零问题【python】

简介: 动态规划Dynamic programming详解-硬币找零问题【python】

作者介绍:10年大厂数据\经营分析经验,现任大厂数据部门负责人。

会一些的技术:数据分析、算法、SQL、大数据相关、python

欢迎加入社区:码上找工作

作者专栏每日更新:

LeetCode解锁1000题: 打怪升级之旅

python数据分析可视化:企业实战案例

备注说明:方便大家阅读,统一使用python,带必要注释,公众号 数据分析螺丝钉 一起打怪升级

问题背景

硬币找零问题是一个经典的动态规划问题,在这个问题中,我们假设有无限数量的n种面额的硬币,要使用这些硬币组成特定金额(M),我们的目标是找到所需硬币数量的最小值。

问题描述

给定:

  • 一个硬币面额的数组 coins(例如 [1, 2, 5])
  • 一个总金额 amount(例如 11)

输出:

  • 组成总金额所需的最小硬币数量(例如 11 = 5 + 5 + 1,输出 3)
  • 如果无法组成该金额,则输出 -1

解题思路

动态规划的解决方案涉及构建一个数组来保存达到每个金额所需的最小硬币数量。我们从金额0开始逐步构建解决方案,直到金额M。

动态规划解法

定义状态

dp[i] 表示组成金额 i 所需的最小硬币数量。

状态转移方程

推导过程

我们知道,如果我们有一种面额为 c 的硬币,那么组成金额 i 可以由组成金额 i - c 再加上一枚面额为 c 的硬币得到。换句话说,如果我们知道了 dp[i - c],我们就可以通过加上一枚 c 面额的硬币来得到 dp[i]

这里有个关键的观察点:要得到 dp[i],我们需要考虑所有小于 i 的金额,并检查增加哪种面额的硬币会导致硬币总数最小。也就是说,我们要考虑所有可能的 c,其中 c 是硬币的面额。

因此,对于每个 cdp[i] 可以这样计算:

  • dp[i - c] + 1,这里的 +1 表示加上一枚面额为 c 的硬币。

由于我们想要的是最小硬币数量,所以我们会选择所有 dp[i - c] + 1 中的最小值。

初始化

我们初始化 dp[0] = 0,因为金额0不需要任何硬币,其他金额初始化为一个较大的数,比如 amount + 1float('inf')

代码示例

# 定义函数来计算组成特定金额所需的最小硬币数量
def coin_change(coins, amount):
    # 用一个大数初始化DP数组,表示初始状态无法达成
    dp = [float('inf')] * (amount + 1)
    # 定义初始状态,0金额需要0硬币
    dp[0] = 0
 
    # 遍历所有金额,从1到目标金额
    for i in range(1, amount + 1):
        # 遍历每个硬币
        for coin in coins:
            # 如果当前硬币可以用(不会使金额变成负数)
            if i - coin >= 0:
                # 状态转移方程
                dp[i] = min(dp[i], dp[i - coin] + 1)
 
    # 如果最终的金额状态仍然是初始化的大数,说明无解,返回-1
    return dp[amount] if dp[amount] != float('inf') else -1
 
# 定义硬币面额和目标金额
coins = [1, 2, 5]
amount = 11
 
# 调用函数并打印结果
result = coin_change(coins, amount)
result

提供解决方案

如果 dp[amount] 是一个较大的数,则表示无解;否则,dp[amount] 就是所需的最小硬币数量。

算法分析

时间复杂度 O(nm):在硬币找零问题中,我们需要计算每个金额从1到目标金额 amount 的最小硬币数。对于每个金额,我们都需要遍历所有的硬币面额来确定最少的硬币数。

  • 假设硬币的面额种类有 n 种。
  • 假设目标金额为 m

对于每个金额 ii 从1到 m),我们需要遍历所有硬币面额,所以时间复杂度为 O(nm)。

空间复杂度 O(m):在硬币找零问题中,我们通常使用一维数组来存储中间结果,这个数组的大小是目标金额 m 加1(为了从0开始计数)。因此,空间复杂度为 O(m)。

图解步骤

假设我们有面额为1, 2, 5的无限硬币,我们需要组成总金额11。

我们初始化 dp[0, inf, inf, ..., inf](长度为12),然后开始填充 dp 数组:

  • dp[1] 最小是1(一个1元硬币)
  • dp[2] 最小是1(一个2元硬币)
  • dp[3] 最小是2(一个1元加一个2元)
  • ...
  • dp[11] 最小是3(两个5元加一个1元)

最后 dp[11] 是3,这是我们的答案。

1.初始状态(没有使用任何硬币):

2.使用1元硬币填充表格:

3.加入2元硬币后的变化(注意金额2,4,6,8,10的变化):

  1. 在只有1元硬币可用时,构成金额 i 需要 i 个硬币,因为1元硬币是唯一的选择。
  2. 加入2元硬币后,我们有机会减少构成某些金额所需的硬币数量。对于任何偶数金额,比如2、4、6等,我们可以用一个2元硬币替代两个1元硬币,这减少了硬币的总数。而对于比如金额3、5、7等含有奇数的金额,我们会尝试使用至少一个1元硬币,加上尽可能多的2元硬币。

4.加入5元硬币后的变化(注意金额5,10的显著减少):

总结

硬币找零问题是动态规划问题中的另一个经典案例。它展示了动态规划如何通过构建子问题的解决方案来解决原问题,并确保每一步都是优化的。这个方法既可以帮助我们找到所需的最小硬币数量,也可以用来说明如果没有可行的方案如何处理。

通过动态规划解决问题时,始终要记得定义清楚状态,找到状态转移方程,并正确地初始化状态。这样,我们就可以构建一个有效的算法来解决一系列复杂的优化问题。


欢迎关注微信公众号 数据分析螺丝钉

相关文章
|
20天前
|
算法 数据挖掘 开发者
LeetCode题目55:跳跃游戏【python5种算法贪心/回溯/动态规划/优化贪心/索引哈希映射 详解】
LeetCode题目55:跳跃游戏【python5种算法贪心/回溯/动态规划/优化贪心/索引哈希映射 详解】
|
20天前
|
SQL 算法 数据可视化
python 贪心算法 动态规划实现 跳跃游戏ll【力扣题45】
python 贪心算法 动态规划实现 跳跃游戏ll【力扣题45】
|
20天前
|
存储 SQL 算法
【动态规划】切割钢条详解python
【动态规划】切割钢条详解python
|
20天前
|
存储 SQL 算法
动态规划Dynamic programming详解-背包问题【python】
动态规划Dynamic programming详解-背包问题【python】
|
20天前
|
存储 SQL 算法
动态规划详解-最小路径和问题【python】
动态规划详解-最小路径和问题【python】
|
存储 算法 决策智能
Python算法题解:动态规划解0-1背包问题
Python算法题解:动态规划解0-1背包问题
|
7天前
|
机器学习/深度学习 人工智能 前端开发
Python中的模块化编程
【6月更文挑战第17天】Python模块化编程与软件架构设计的关键在于拆分任务到独立模块,提高代码的可维护性、可重用性和可扩展性。例如,学生管理系统可分解为录入、查询和删除模块。MVC和MVVM架构模式有助于组织代码,而微服务和函数式编程将在未来发展中扮演重要角色。通过示例代码,读者能学习如何实现这些概念,提升项目开发效率和质量。
155 57
|
14天前
|
测试技术 虚拟化 云计算
GitHub高赞!速通Python编程基础手册,被玩出花了!
随着云时代的来临,Python 语言越来越被程序开发人员喜欢和使用,因为其不仅简单易学,而且还有丰富的第三方程序库和相应完善的管理工具。 从命令行脚本程序到 GUI程序,从图形技术到科学计算,从软件开发到自动化测试,从云计算到虚拟化,所有这些领域都有 Python 的身影。 今天给小伙伴们分享的这份手册采用以任务为导向的编写模式,全面地介绍了 Python 编程基础及其相关知识的应用,讲解了如何利用 Python 的知识解决部分实际问题。
GitHub高赞!速通Python编程基础手册,被玩出花了!
|
4天前
|
数据挖掘 数据处理 Python
Python编程入门:从基础到实践
【6月更文挑战第26天】这篇文章引导读者逐步学习Python编程,从基础语法如变量、数据类型(整数、浮点数、字符串)到条件语句、循环(if/for/while),再到函数定义和模块导入。通过实例展示了Python在文本处理、数据分析(使用pandas)和Web开发(使用Flask)的应用。学习Python能为初学者开启更广阔的技术领域,如面向对象编程、并发和网络编程等。
|
2天前
|
设计模式 程序员 测试技术
老程序员分享:Python数据模型及Pythonic编程
老程序员分享:Python数据模型及Pythonic编程
10 1