Python零钱兑换的实现代码

目录
  • 题目:
  • 题目分析:
  • 解题思路:
    • 解法一:递归
      • 代码实现
      • 代码注释
    • 解法二:

题目:

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

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

示例 1:

输入:coins = [1, 2, 5], amount = 11

输出:3

解释说明:11 = 5 + 5 + 1

示例 2:

输入:coins = [2], amount = 3

输出:-1

解释说明:硬币无法凑成金额-1

示例 3:

输入:coins = [1], amount = 0

输出:0

题目分析:

题目要求用最少的硬币个数凑出总金额amount。我们第一感觉可能是使用暴力或者递归解题,对这道题使用暴力解题,计算出所有可能的结果后取硬币数最小值,时间复杂度妥妥O(n^3),算是很慢的解题方式了,下面我们介绍递归解法和玄学位运算解法(使用到位运算解法的解题效率一半很高,但是很难想到,所以我愿称之为“玄学”)

解题思路:

解法一:递归

使用动态规划五部曲

1.分析确定dp数组以及其下标的含义或状态分析

我们规定dp[i]表示凑足总额为  i  所需钱币的最少个数。

2.确定递推公式

我们考虑dp[i]的来源,因为dp[i]的来源为dp[i - coins[i]] + 1,(coins[i]表示coins中的第i枚硬币),这也是dp[i]的唯一来源。

那为什么要+1呢?

这里我们明确dp[i - coins[i]]是凑够金额 i - coin[i]的最少硬币个数。那么当金额i - coin[i]变到 i 时,意味着我们在coins中拿了一枚硬币coins[i],那么从dp[i - coin[i]] 到 dp[i]需要加上所取得那枚硬币,即+1.

分析到dp[i]状态及前面得状态,dp[i]即为最优解。

---------------------------------------

coins = [1, 2, 3]   amount = 5

那么在 1+1+1+1+1 = 5, 1+2+1+1 = 5, 2+2+1 = 5....等情况中

dp[5]最优解必为2+2+1 = 5

即dp[5] = dp[5 - coins[0]] + 1

而dp[5 - coins[0]] = dp[4] = dp[4 - coins[1]] + 1

以此类推

------------------------------------------

我们要取最优解(硬币数最少)也就是取dp[i - coins[i]] + 1最小值

即递推公式为:dp[i] = min(dp[i - coins[i]] + 1, dp[i])

(括号中得dp[i]为上一状态的dp[i])

3.如何初始化dp数组

我们分析公式的基础,可得公式基础为dp[0]即凑足总额为  0  所需钱币的最少个数。接着考虑到其他dp列表其他下标的初始化,由于递推公式使用了min(),那么为了不让初始化影响递推结果,我们需要将dp[i](i != 0)初始化为一个很大的数,如正无穷‘inf’。

4.确定遍历的顺序

题目要求的是找到最小硬币个数,所以遍历coins或者先遍历寻找amount列表无关紧要。

5.举例验证推导的dp数组(公式)是否正确

可以带入一个简单以的例子,比如例1.

代码实现

def coinChange(coins, amount):
    dp = [float('inf')] * (amount + 1)
    dp[0] = 0
    for coin in coins:
        for i in range(coin, amount + 1):
            dp[i] = min(dp[i], dp[i - coin] + 1)
    return dp[amount] if dp[amount] != float('inf') else -1

代码注释

def coinChange(coins, amount):
    # 初始化dp列表
    dp = [float('inf')] * (amount + 1)
    dp[0] = 0  # 初始化递推公式基础
    for coin in coins:   # 遍历硬币
        # 遍历寻找构成amount最优解
        for i in range(coin, amount + 1):
            dp[i] = min(dp[i], dp[i - coin] + 1)
    # 如果最终没有找到凑成amount金额的硬币,返回-1
    return dp[amount] if dp[amount] != float('inf') else -1

时间复杂度O(nm),n为amoun面额,m为硬币种数。空间复杂度为O(m),即为dp列表所用空间。

解法二:

接下来就是玄学位运算了。先看代码

代码实现

def coinChange(coins, amount):
    if not amount:
        return 0
    dp = 1 << amount
    res = 0
    while dp:
        tmp = 0
        res += 1
        for i in coins:
            tmp |= dp >> i
        if tmp & 1:
            return res
        dp = tmp
    return -1

代码注释

def coinChange(coins, amount):
    if not amount:
        return 0
    # 按位左移运算构造类似dp数组的记录二进制
    dp = 1 << amount
    res = 0
    while dp:  # dp = 0或return 时循环结束
        tmp = 0  # tmp用于临时记录和承接上一个dp二进制
        res += 1  # res为最终答案
        for i in coins:
            # 利用按位右移不断右移。利用按位或运算
            # 将前一次按位右移运算与后一次按位右移运算合并
            tmp |= dp >> i
        if tmp & 1:  # 当tmp最后位数为1时res即为答案,返回res
            return res
        dp = tmp
    return -1

位运算解法过程我打印出来了,不清楚的可以看看

def coinChange(coins, amount):
    if not amount:
        return 0
    dp = 1 << amount
    res = 0
    while dp:
        print('dp:', bin(dp))
        tmp = 0
        print('tmp:', bin(tmp))
        res += 1
        print('res:', res)
        for i in coins:
            print('i:', i)
            tmp |= dp >> i
            print('ys_tmp:', bin(tmp))
            print('--------------')
        if tmp & 1:
            return res
        dp = tmp
    return -1

输出

dp: 0b100000000000
tmp: 0b0
res: 1
i: 1
ys_tmp: 0b10000000000
--------------
i: 2
ys_tmp: 0b11000000000
--------------
i: 5
ys_tmp: 0b11001000000
--------------
dp: 0b11001000000
tmp: 0b0
res: 2
i: 1
ys_tmp: 0b1100100000
--------------
i: 2
ys_tmp: 0b1110110000
--------------
i: 5
ys_tmp: 0b1110110010
--------------
dp: 0b1110110010
tmp: 0b0
res: 3
i: 1
ys_tmp: 0b111011001
--------------
i: 2
ys_tmp: 0b111111101
--------------
i: 5
ys_tmp: 0b111111101
--------------

虽然难理解,但是解题效率不是一般的高

时间复杂度O(n),n为coins长度。空间复杂度O(1),使用有限变量。

到此这篇关于Python零钱兑换的实现代码的文章就介绍到这了,更多相关Python零钱兑换内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Python实现的一个找零钱的小程序代码分享

    Python写的一个按面值找零钱的程序,按照我们正常的思维逻辑从大面值到小面值的找零方法,人民币面值有100元,50元,20元,10元,5元,1元,5角,1角,而程序也相应的设置了这些面值.只需要调用函数时传入您想要找零的金额,程序会自动算各个面值的钱应该找多少张.如传入50元,则系统自动算出找零50元一张面值,如果传入60块7毛,则程序自动算出该找零50元一张,10元一张,5角一张,1角两张. # encoding=UTF-8   def zhaoqian(money):     loop=T

  • Python 硬币兑换问题

    硬币兑换问题: 给定总金额为A的一张纸币,现要兑换成面额分别为a1,a2,....,an的硬币,且希望所得到的硬币个数最少. # 动态规划思想 dp方程式如下 # dp[0] = 0 # dp[i] = min{dp[i - coins[j]] + 1}, 且 其中 i >= coins[j], 0 <= j < coins.length # 回溯法,输出可找的硬币方案 # path[i] 表示经过本次兑换后所剩下的面值,即 i - path[i] 可得到本次兑换的硬币值. def ch

  • Python零钱兑换的实现代码

    目录 题目: 题目分析: 解题思路: 解法一:递归 代码实现 代码注释 解法二: 题目: 给你一个整数数组 coins ,表示不同面额的硬币:以及一个整数 amount ,表示总金额. 计算并返回可以凑成总金额所需的 最少的硬币个数 .如果没有任何一种硬币组合能组成总金额,返回 -1 .你可以认为每种硬币的数量是无限的. 示例 1: 输入:coins = [1, 2, 5], amount = 11 输出:3 解释说明:11 = 5 + 5 + 1 示例 2: 输入:coins = [2], a

  • 用python下载百度文库的代码

    先去下载一个叫SWFToImage.dll的东西 再建立一个bat文件,并运行: 复制代码 代码如下: COPY SWFToImage.dll %windir%\system32 regsvr32 %windir%\system32\SWFToImage.dll 复制代码 代码如下: #用python下载百度文库的代码,需要的同志请修改,下面有提示 #http://www.cnblogs.com/dearplain/ #code by plain import urllib2 import wi

  • python编程羊车门问题代码示例

    问题: 有3扇关闭的门,一扇门后面停着汽车,其余门后是山羊,只有主持人知道每扇门后面是什么.参赛者可以选择一扇门,在开启它之前,主持人会开启另外一扇门,露出门后的山羊,然后允许参赛者更换自己的选择. 请问: 1.按照你的第一感觉回答,你觉得不换选择能有更高的几率获得汽车,还是换选择能有更高的几率获得汽车?或几率没有发生变化? 答:第一感觉换与不换获奖几率没有发生变化. 2.请自己认真分析一下"不换选择能有更高的几率获得汽车,还是换选择能有更高的几率获得汽车?或几率没有发生变化?" 写出

  • Python探索之pLSA实现代码

    pLSA(probabilistic Latent Semantic Analysis),概率潜在语义分析模型,是1999年Hoffman提出的一个被称为第一个能解决一词多义问题的模型,通过在文档与单词之间建立一层主题(Topic),将文档与单词的直接关联转化为文档与主题的关联以及主题与单词的关联.这里采用EM算法进行估计,可能存在差错,望积极批评指正. # -*- coding: utf-8 -*- import math import random import jieba import c

  • Python单链表简单实现代码

    本文实例讲述了Python单链表简单实现代码.分享给大家供大家参考,具体如下: 用Python模拟一下单链表,比较简单,初学者可以参考参考 #coding:utf-8 class Node(object): def __init__(self, data): self.data = data self.next = None class NodeList(object): def __init__(self, node): self.head = node self.head.next = No

  • Python手机号码归属地查询代码

    简单的一个例子,是以前用Dephi写的,前不久刚实现了一个在Python中使用Delphi控件来编写界面程序,于是趁热写一个类似的的查询方案. 本实例是通过www.ip138.com这个网站来查询的,这里需要的几个知识点,就是用Python模拟网页提交数据,获得数据返回信息,以及对返回的Html信息进行解析,模拟Http提交,Python自带有一个urllib和urllib2这两个库,相当方便,只是奇怪,为什么不将两个库合并成一个,这样来的更方便.然后就是窗体了,窗体还是用我之前写的一个Pyth

  • python 创建弹出式菜单的实现代码

    python 创建弹出式菜单的实现代码            实现效果图: Python代码  import win32ui import win32api from win32con import * from pywin.mfc import window class MyWnd(window.Wnd): def __init__ (self): window.Wnd.__init__(self,win32ui.CreateWnd()) self._obj_.CreateWindowEx(W

  • Python多进程同步简单实现代码

    本文讲述了Python多进程同步简单实现代码.分享给大家供大家参考,具体如下: #encoding=utf8 from multiprocessing import Process, Lock def func(lock, a): lock.acquire() print a lock.release() if __name__ == '__main__': lock = Lock() workers = [] # 创建两个进程 for i in range(0, 2): p = Process

  • python解析xml模块封装代码

    有如下的xml文件: 复制代码 代码如下: <?xml version="1.0" encoding="utf-8" ?>  <root>  <childs>  <child name='first' >1</child>  <child value="2">2</child>  </childs>  </root> 下面介绍python解

  • Python处理Excel文件实例代码

    因为工作需求,需要审核一部分query内容是否有效,query储存在Excel中,文本内容为页面的Title,而页面的URL以HyperLink的格式关联到每个Cell. 于是本能的想到用Python读取Excel文件之后进行文本分析,之后对每个链接进行一次HttpRequest,通过分析HttpResponse的内容来判断当前链接是否有效. 于是上网搜了下,发现比较主流的是用xlrd的插件,但是实际使用过程中发现,无论如何,最终获取的hyperlink_map值一直都是None,也没空去分析到

随机推荐