硬币找零问题

硬币找零问题:假设需要找零的金额为C,最少要用多少面值为p1<p2<…<pn的硬币(面值种类为n,且假设每种面值的硬币都足够多)​?

贪心算法的基本原理是:遵循某种既定原则,不断地选取当前条件下最优的选择来构造每一个子步骤,直到获得问题最终的求解。即在求解时,总是作出当前看来最好的选择

贪心选择性质是指应用同一规则,将原问题变为一个相似的但规模更小的子问题,而后的每一步都是当前看似最佳的选择。这种选择依赖于已做出的选择,但不依赖于未做出的选择。从全局来看,运用贪心策略解决的问题在程序的运行过程中无回溯过程。贪心选择性质的证明一般采用数学归纳法,证明每一步做出的贪心选择确实能导致问题的整体(全局)最优解,也有基于算法的输出,或使用一种“拟阵”结构等形式的证明。

注意,贪心算法利用问题的贪心性质,简化了分解原始问题的过程,每次只关注在当前状态下可以获得的局部最优解,通过拼接各阶段的局部最优解获得最终问题的解。因为贪心选择可以依赖于以往所做的选择,但绝不依赖于未来所做的选择,也不依赖于子问题的解。故而贪心算法往往求解时与动态规划相反,采用自顶向下的方式,迭代作出贪心选择,不断化简问题规模。

问题实例:假设要找零8元,市面上有3种不同面值的硬币,各硬币的面值分别为1元、3元、4元。

可以先使用动态规划思路进行求解:

F(C)=min{F(C-pi)}+1,C>0且C≥pi

初始条件:F(0)=0

F(1)=min{F(1-1)}+1=1;

F(2)=min{F(2-1)}+1=2;

F(3)=min{F(3-1),F(3-3)}+1=1;

F(4)=min{F(4-1),F(4-3),F(4-4)}+1=1;

F(5)=min{F(5-1),F(5-3),F(5-4)}+1=2;

F(6)=min{F(6-1),F(6-3),F(6-4)}+1=2;

F(7)=min{F(7-1),F(7-3),F(7-4)}+1=2;

F(8)=min{F(8-1),F(8-3),F(8-4)}+1=2。

因此最少需要2枚硬币即可找零8元。

当使用贪心算法在计算的时候,从最大面值的硬币开始,直接得出答案2枚4元硬币。

注意,虽然贪心算法看似非常简便迅速,但是它不总是有效的。比如,当要找零6元时,它得到的答案是1枚4元硬币和2枚1元硬币,即最少3枚硬币。而动态规划得到的正确答案是2枚3元硬币。这个时候贪心算法不再适用,应选用动态规划等其他算法进行求解。

现在假设市面上有6种不同面值的硬币,各硬币的面值分别为5分、1角、2角、5角、1元、2元,要找零10.5元,求出最少硬币的数量。

从面值最大的硬币开始遍历:

需要5枚2元硬币,剩下0.5元。

再加1枚5角硬币。

一共6枚硬币。

def getChange(coins, amount):
    coins.sort()
    # 从面值最大的硬币开始遍历
    i = len(coins) - 1
    while i >= 0:
        if amount >= coins[i]:
            n = int(amount // coins[i])
            change = n * coins[i]
            amount -= change
            amount = round(amount, 2)  # 四舍五入到小数点第2位
            print(n, '枚', coins[i])
        i -= 1
    print()

输入:

输出:

输入:

输出:

### 硬币找零问题的递归算法实现及解析 硬币找零问题是经典的计算机科学问题之一,其目标是在给定面额的硬币集合中找到最少数量的硬币组合以构成特定金额。递归是一种自然适合该问题的方法,因为它可以通过分解子问题的方式逐步逼近最终解。 #### 1. 递归算法的核心思想 递归算法通过将原问题拆分为更小规模的子问题来解决问题。对于硬币找零问题而言,假设当前需要凑成的目标金额为 `amount`,可用硬币集合为 `[c1, c2, ..., cn]`,那么最小硬币数可以通过如下方式定义: 如果 `amount == 0`,则返回 0 表示不需要任何硬币即可完成; 如果 `amount < 0` 或者没有剩余硬币,则表示无法完成,返回无穷大作为标记; 否则尝试每一种可能的硬币选择,并取其中所需的最小硬币数目加一作为结果[^4]。 #### 2. Python 实现代码 以下是基于上述逻辑编写的递归版本硬币找零问题解决方案: ```python def coin_change_recursive(coins, amount): # 边界条件处理 if amount == 0: return 0 if amount < 0: return float('inf') # 返回正无穷 min_coins = float('inf') for coin in coins: sub_problem_result = coin_change_recursive(coins, amount - coin) if sub_problem_result != float('inf'): min_coins = min(min_coins, sub_problem_result + 1) return min_coins # 测试用例 coins = [1, 2, 5] amount = 11 result = coin_change_recursive(coins, amount) print(f"Minimum number of coins required to make {amount}: {'Impossible' if result == float('inf') else result}") ``` 此代码片段展示了如何利用递归来解决硬币找零问题。它遍历所有可能的选择路径并选取最佳方案。 #### 3. 时间复杂度分析 由于每次调用都会针对每一个硬币生成新的子问题实例,因此时间复杂度呈指数增长 O(n^m),这里 n 是硬币种类的数量而 m 则代表总金额大小。这种效率低下主要源于大量重复计算的存在——许多相同的子状态被多次评估。 为了改善性能表现,可以引入 **记忆化搜索** 技术(Memoization),即保存已经求解过的子问题的结果从而避免冗余运算。这实际上就是动态规划的一种形式。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

algorithm6

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值