Python动态规划之零钱兑换问题详解

 更新时间:2023年11月03日 09:44:43   作者:惊瑟  
这篇文章主要介绍了Python动态规划之零钱兑换问题详解,这次我们就按照套路模板,再来剖析一道经典动规题目零钱兑换,计算并返回可以凑成总金额所需的 最少的硬币个数 如果没有任何一种硬币组合能组成总金额,返回-1,需要的朋友可以参考下

问题描述

给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。

计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。

你可以认为每种硬币的数量是无限的。

问题分析

考察其是否满足动态规划的两个特征:

是否求最值?显而易见,这是一个求最小值的问题;

是否具有最优子结构?考察大规模问题的解是否可以由小规模问题的解推导出来。我们可以假设amount<n所需的最小硬币数量都是已知的,那如何得出amount=n所需的最小硬币数量呢?可以结合具体场景,如果你手上有1块、2块面额的硬币,要求凑出总额amount =5的所需最小硬币个数,而amount <5的所需最小硬币个数是已知的。很容易想到,只需要在amount =3或者amount =4的所需最小硬币个数的基础上再加1个硬币(2块或者1块)就可以得到amount =5所需的硬币个数了(注意,这里还不是最小硬币个数),再取这两种情况的最小值,便可以得到amount =5的所需的最小硬币个数了(是不是想起了跳台阶问题?)。

以上分析我们可以得出,该问题是动态规划问题。

求解套路

明确有哪些状态。很容易想到,在状态转化过程(大规模问题由小问题规模问题推导的过程)中,总金额amount 一定是发生变化的,因此amount 是状态。

明确dp数组含义。根据求什么设什么原则,我们可以设dp代表最少硬币数量,由于只有amount一个状态,因此dp为1维。综上,dp应设为dp[n],代表凑出总额为n需要的最少数量金币。

状态转移方程。有了【问题分析】中的例子,相信找出状态转移方程并不难,直接贴结论:

初始化dp。由于求的是最小值,因此要反着来,初始化为最大。考虑到coins是正整数数组,即coin最小是1,所以对于总金额n,最坏情况下(即只用面值为1的硬币)需要n个硬币。我们需要将dp初始化为正常情况下取不到的值,因此我们将dp其初始化为n+1。

代码

def coin_change(coins,amount):

    # 判断边界值
    if amount == 0:
        return 0

    # 初始化dp数组,长度是amount+1,因为0~n一共有n+1个元素
    dp = [amount+1]*(amount+1)
    # 因为dp[n]要靠dp[0]推导,所以dp[0]需要按实际情况初始化为0
    dp[0] = 0

    # 遍历状态
    for i in range(1,amount+1):
        # 注意coins并不是状态,只是我们状态转移方程需要遍历它取最小值
        for coin in coins:
            if i-coin >= 0:
                dp[i] = min(dp[i],dp[i-coin]+1)
    if dp[amount] == amount+1: # 若为真,说明无解
        return -1
    else:
        return dp[amount]


if __name__ == '__main__':
	
    # 测试用例,来自leecode #322题
    eg =[[[1,2,5],11],[[2],3],[[1],0],[[1],1],[[1],2]]
    for coins,amount in eg:
        print(coin_change(coins,amount),end='  ')

算法复杂度分析

  • 时间复杂度

显然是O(nm),其中n为amount,即总金额,m为硬币coins的种类。

  • 空间复杂度

由于我们使用长度为amount+1的数组dp来保存状态,因此为O(n)。

到此这篇关于Python动态规划之零钱兑换问题详解的文章就介绍到这了,更多相关Python零钱兑换问题内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Python中Celery异步任务队列的具体使用

    Python中Celery异步任务队列的具体使用

    Celery是一个用于处理分布式任务和作业队列的异步任务队列库,本文主要介绍了Python中Celery异步任务队列的具体使用,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧
    2024-02-02
  • 解析python调用函数加括号和不加括号的区别

    解析python调用函数加括号和不加括号的区别

    这篇文章主要介绍了python调用函数加括号和不加括号的区别,不带括号时,调用的是这个函数本身 ,是整个函数体,是一个函数对象,不须等该函数执行完成,具体实例代码跟随小编一起看看吧
    2021-10-10
  • python生成xml时规定dtd实例方法

    python生成xml时规定dtd实例方法

    在本篇文章里小编给大家整理的是关于python生成xml时规定dtd实例方法,需要的朋友们学习参考下。
    2020-09-09
  • Python3 把一个列表按指定数目分成多个列表的方式

    Python3 把一个列表按指定数目分成多个列表的方式

    今天小编就为大家分享一篇Python3 把一个列表按指定数目分成多个列表的方式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-12-12
  • 使用Python的pygame库实现下雪效果的示例代码

    使用Python的pygame库实现下雪效果的示例代码

    这篇文章给大家介绍了如何使用Python的pygame库实现下雪的效果,文中通过代码示例介绍的非常详细,对大家的学习或工作有一定的的帮助,需要的朋友可以参考下
    2024-01-01
  • 基于Python绘制520表白代码

    基于Python绘制520表白代码

    这周五就是520,大家都准备好送给女朋友的礼物了吗?快来利用Python编写个表白代码送给她吧!文中示例代码讲解详细,跟随小编一起动手试一试吧
    2022-05-05
  • Django的信号机制详解

    Django的信号机制详解

    Django中提供了“信号调度”,用于在框架执行操作时解耦。通俗来讲,就是一些动作发生的时候,信号允许特定的发送者去提醒一些接受者。
    2017-05-05
  • python实现npy格式文件转换为txt文件操作

    python实现npy格式文件转换为txt文件操作

    这篇文章主要介绍了python实现npy格式文件转换为txt文件操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-07-07
  • NumPy实现结构化数组的示例代码

    NumPy实现结构化数组的示例代码

    结构化数组是 NumPy 中用于处理异质数据的重要工具,通过定义复杂的数据类型,我们可以创建具有不同字段的数组,本文主要介绍了NumPy实现结构化数组的示例代码,具有一定的参考价值,感兴趣的可以了解一下
    2024-01-01
  • 对python中两种列表元素去重函数性能的比较方法

    对python中两种列表元素去重函数性能的比较方法

    今天小编就为大家分享一篇对python中两种列表元素去重函数性能的比较方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-06-06

最新评论