算法设计与分析:Coin Change(Week 12)

本文介绍了322题Coin Change的动态规划解决方案,通过自底向上的方法避免重复计算,降低时间复杂度。文章提供详细算法步骤及Python3代码实现,分析了时间复杂度和空间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

学号:16340008

题目:322. Coin Change


Question:

You are given coins of different denominations and a total amount of money amount. Write a function to compute the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return -1.

Example 1:

Input: coins = [1, 2, 5], amount = 11
Output: 3 
Explanation: 11 = 5 + 5 + 1

Example 2:

Input: coins = [2], amount = 3
Output: -1

Note:
You may assume that you have an infinite number of each kind of coin.


Answer:

题意是我们有不同价值的硬币,以及一个数amount。需要求如何用最少的硬币数使其总价值为amount(若能凑出),否则返回-1。考虑到比较容易想到的动态规划的办法,就是将求凑出一个amount0需要的硬币数,且amount0能用一个硬币就得到amount,于是F(amount) = F(amount0) + 1

于是我们有了如下算法:

  1. 对于一个amount,遍历硬币种类n,得到一个数组num[n],其每个元素都是amount - coin,coin是其中一个硬币
  2. 令F(amount)为F(num[i])(0 <= i < n)中的最小值+1,对求每个num[i]使用步骤1,2

以上是初步的方法,但是我们知道对于以上方法一个F(amount0)可能被求多次,导致时间的浪费,我们可以十分习惯的使用一个数组array[amount-1]来记录我们求过的F(num)。

因此我们可以得到改进的方法:

我们先初始化array[]为0,而且考虑自底向上。自底向上可以使模型更直观,而且我们能保证求的元素之前的元素都已是最佳方案。于是得到算法如下:

  1. 初始化数组array[amount+1]为一个足够大的值(无穷大或amount+1),且array[0] = 0。对array[1]到array[amount]作步骤2
  2. coins中每个coin作步骤3
  3. 如果coin <= amount,则array[i]赋值为array[i]与array[i-coin]+1中的较小值。
  4. 完成步骤1,2,3后,检查array[amount]不是原来的大值,是的话则返回array[amount],否则返回-1(没有凑出amount的方法)

此时的时间复杂度十分明显,我们知道步骤1和2分别是两个for,1嵌套2,因此时间复杂度就是O(count * amount),而空间复杂度就是数组长度(O(amount))。我们能得到以下代码(python3):

class Solution:
    def coinChange(self, coins, amount):
        """
        :type coins: List[int]
        :type amount: int
        :rtype: int
        """
        array = [amount + 1] * (amount + 1)
        array[0] = 0

        for i in range(1, amount + 1):
            for coin in coins:
                if (coin <= i):
                    array[i] = min(array[i], array[i - coin] + 1)
        
        if array[amount] != amount + 1:
            return array[amount]
        else:
            return -1

本地测试代码:

coins = [1, 2, 5]
amount = 11
test = Solution()
print(test.coinChange(coins, amount))

提交结果:

### 贪心算法思想的应用 贪心算法是一种在每一步选择中都采取当前状态下局部最优的选择,从而希望最终获得全局最优解的算法策略[^1]。然而需要注意的是,这种算法并不总是能够保证得到真正的全局最优解,但在某些特定问题上却非常有效并能提供接近最优的结果[^3]。 #### 1. 局部最优全局最优的关系 贪心算法的核心在于其决策过程只关注于当下的最佳选择而不考虑未来的可能影响。例如,在解决车辆路径问题时,每次都优先选择距离当前位置最近的未访问客户点作为下一步的目标,这种方法虽然简单高效,但可能会因缺乏对未来情况的整体考量而导致次优解[^4]。 #### 2. 实际案例解析 以下是几个常见的基于贪心算法的实际应用场景及其对应的实现逻辑: - **活动安排问题** 这是一个经典的利用贪心方法求解的问题实例之一。假设存在多个相互冲突的时间段内的任务集合S={a₁,a₂,...,an},其中每个ai都有各自的起始时间和结束时间si和fi,则可以通过按照最早完成时间顺序排列这些活动来进行筛选,直到无法再加入新的不重叠活动为止。 ```python def activity_selection(activities): activities.sort(key=lambda x:x[1]) # 按照结束时间升序排序 selected = [] last_finish_time = float('-inf') for act in activities: start_time, finish_time = act if start_time >= last_finish_time: selected.append(act) last_finish_time = finish_time return selected ``` - **找零钱问题** 给定面额分别为c₁,c₂,…cn 的硬币无限量供应以及金额V ,寻找最少数量的硬币组合成该金额 V 。此问题是另一个典型体现贪婪思维的例子。通常情况下我们会先尝试使用最大可用面值去凑足总额度直至剩余部分不足以容纳更大尺寸为止。 ```python def coin_change(coins, amount): coins.sort(reverse=True) # 对硬币按降序排列 count = 0 remaining_amount = amount for c in coins: num_c = remaining_amount // c count += num_c remaining_amount -= num_c * c if not remaining_amount: break return count if not remaining_amount else -1 ``` #### 3. 结论总结 尽管如此,仍需注意并非所有的优化难题都可以借助上述方式轻易化解;对于那些复杂程度较高的情形而言,单纯依赖单一维度上的极值选取往往难以奏效,此时则有必要引入动态规划或者分支限界等更为复杂的计算框架加以辅助处理[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值