动态规划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的显著减少):

总结

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

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


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

相关文章
|
6月前
|
算法 开发者 Python
惊呆了!Python算法设计与分析,分治法、贪心、动态规划...这些你都会了吗?不会?那还不快来学!
【7月更文挑战第10天】探索编程巅峰,算法至关重要。Python以其易读性成为学习算法的首选。分治法,如归并排序,将大问题拆解;贪心算法,如找零问题,每步求局部最优;动态规划,如斐波那契数列,利用子问题解。通过示例代码,理解并掌握这些算法,提升编程技能,面对挑战更加从容。动手实践,体验算法的神奇力量吧!
78 8
|
2月前
|
算法 Python
在Python编程中,分治法、贪心算法和动态规划是三种重要的算法。分治法通过将大问题分解为小问题,递归解决后合并结果
在Python编程中,分治法、贪心算法和动态规划是三种重要的算法。分治法通过将大问题分解为小问题,递归解决后合并结果;贪心算法在每一步选择局部最优解,追求全局最优;动态规划通过保存子问题的解,避免重复计算,确保全局最优。这三种算法各具特色,适用于不同类型的问题,合理选择能显著提升编程效率。
69 2
|
3月前
|
存储 算法 Python
【10月更文挑战第16天】「Mac上学Python 27」小学奥数篇13 - 动态规划入门
本篇将通过 Python 和 Cangjie 双语介绍动态规划的基本概念,并解决一个经典问题:斐波那契数列。学生将学习如何使用动态规划优化递归计算,并掌握编程中的重要算法思想。
117 3
|
6月前
|
算法 Python
Python算法高手进阶指南:分治法、贪心算法、动态规划,掌握它们,算法难题迎刃而解!
【7月更文挑战第10天】探索Python算法的精华:分治法(如归并排序)、贪心策略(如找零钱问题)和动态规划(解复杂问题)。通过示例代码揭示它们如何优化问题解决,提升编程技能。掌握这些策略,攀登技术巅峰。
155 2
|
6月前
|
算法 程序员 Python
算法小白到大神的蜕变之路:Python分治法、贪心、动态规划,一步步带你走向算法巅峰!
【7月更文挑战第9天】探索算法之旅,以Python解锁编程高手之路。分治法如二分查找,将复杂问题拆解;贪心算法解决活动选择,每次选取局部最优;动态规划求斐波那契数列,避免重复计算,实现全局最优。每一步学习,都是编程能力的升华,助你应对复杂挑战,迈向算法大师!
52 1
|
6月前
|
存储 算法 Python
Python算法界的秘密武器:分治法巧解难题,贪心算法快速决策,动态规划优化未来!
【7月更文挑战第9天】Python中的分治、贪心和动态规划是三大关键算法。分治法将大问题分解为小问题求解,如归并排序;贪心算法每步选局部最优解,不保证全局最优,如找零钱;动态规划存储子问题解求全局最优,如斐波那契数列。选择合适算法能提升编程效率。
78 1
|
存储 算法 决策智能
Python算法题解:动态规划解0-1背包问题
Python算法题解:动态规划解0-1背包问题
|
1月前
|
人工智能 数据可视化 数据挖掘
探索Python编程:从基础到高级
在这篇文章中,我们将一起深入探索Python编程的世界。无论你是初学者还是有经验的程序员,都可以从中获得新的知识和技能。我们将从Python的基础语法开始,然后逐步过渡到更复杂的主题,如面向对象编程、异常处理和模块使用。最后,我们将通过一些实际的代码示例,来展示如何应用这些知识解决实际问题。让我们一起开启Python编程的旅程吧!
|
1月前
|
存储 数据采集 人工智能
Python编程入门:从零基础到实战应用
本文是一篇面向初学者的Python编程教程,旨在帮助读者从零开始学习Python编程语言。文章首先介绍了Python的基本概念和特点,然后通过一个简单的例子展示了如何编写Python代码。接下来,文章详细介绍了Python的数据类型、变量、运算符、控制结构、函数等基本语法知识。最后,文章通过一个实战项目——制作一个简单的计算器程序,帮助读者巩固所学知识并提高编程技能。