【LeetCode】动态规划—518. 零钱兑换 II(附完整Python/C++代码)

Python3.8

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

题目描述

在这里插入图片描述

前言

零钱兑换 II 是一个经典的动态规划问题。给定几种不同面额的硬币和一个总金额 amount,要求找出可以凑成总金额的不同硬币组合的数量。每种硬币可以使用无限次。该问题与常见的 背包问题 相似,但不同的是这里的每种硬币可以重复使用。


基本思路

1. 问题定义

给定一个整数数组 coins 表示不同面额的硬币,以及一个整数 amount,表示需要凑成的金额。要求计算出能够凑成总金额 amount 的不同组合的数量。

举例:

  • 输入:coins = [1, 2, 5], amount = 5
  • 输出:4
  • 解释:有 4 种方式可以凑成 5:
    1. 5 = 5
    2. 5 = 2 + 2 + 1
    3. 5 = 2 + 1 + 1 + 1
    4. 5 = 1 + 1 + 1 + 1 + 1

2. 理解问题和递推关系

动态规划思想:

我们可以通过动态规划的方法解决该问题。对于每一个硬币,我们依次考虑它是否可以加入到组合中,并且每个硬币可以使用多次。我们定义 dp[i] 表示凑成金额 i 的组合数。

状态定义:

  • dp[i] 表示凑成金额 i 的组合数。

状态转移方程:

  • 对于每个硬币 coin,遍历从 coinamount 的所有金额 i,并更新 dp[i]
    d p [ i ] + = d p [ i − c o i n ] dp[i] += dp[i - coin] dp[i]+=dp[icoin]
    • dp[i] 是凑成金额 i 的组合数。
    • dp[i - coin] 表示当前金额减去硬币面值后的剩余金额可以凑成的组合数。

边界条件:

  • dp[0] = 1,表示凑成金额 0 有 1 种方式(即不选任何硬币)。

动态规划思路:

  1. 初始化 dp 数组,dp[0] = 1
  2. 对于每个硬币 coin,更新 dp 数组中的所有元素。
  3. 最终结果为 dp[amount]

3. 解决方法

动态规划方法

  1. 初始化:创建一个大小为 amount + 1dp 数组,dp[0] = 1 表示凑成金额 0 的方式为 1 种(即不使用任何硬币)。
  2. 状态转移:遍历每个硬币,对于每个硬币,从金额 coin 开始,逐步更新 dp[i],即通过递推公式 dp[i] += dp[i - coin]
  3. 返回结果dp[amount] 即为凑成 amount 的所有不同组合的数量。

伪代码:

initialize dp array with dp[0] = 1
for each coin in coins:
    for i from coin to amount:
        dp[i] += dp[i - coin]
return dp[amount]

4. 进一步优化

  • 时间复杂度:时间复杂度为 O(amount * n),其中 n 为硬币种类的数量,amount 为目标金额。
  • 空间复杂度:空间复杂度为 O(amount),因为我们只需要一个大小为 amount + 1dp 数组。

5. 小总结

  • 递推思路:通过动态规划,每个硬币的组合数可以通过之前的状态推导出来,状态转移方程 dp[i] += dp[i - coin] 说明了当前组合数来源于之前的组合情况。
  • 时间复杂度:时间复杂度为 O(amount * n),适合处理中等规模的输入。

以上就是零钱兑换 II问题的基本思路。


Python代码

class Solution:
    def change(self, amount: int, coins: list[int]) -> int:
        # 初始化dp数组,dp[i]表示凑成金额i的组合数
        dp = [0] * (amount + 1)
        dp[0] = 1  # 凑成金额0的方式有1种(不选任何硬币)

        # 动态规划计算
        for coin in coins:
            for i in range(coin, amount + 1):
                dp[i] += dp[i - coin]
        
        return dp[amount]  # 返回凑成金额amount的组合数

Python代码解释

  1. 初始化:定义 dp 数组,其中 dp[i] 表示凑成金额 i 的组合数,初始时 dp[0] = 1
  2. 动态规划递推:通过遍历每个硬币 coin,逐步更新 dp[i],使其表示所有可能的组合数。
  3. 返回结果:最终返回 dp[amount],即凑成 amount 的所有组合数量。

C++代码

#include <vector>
using namespace std;

class Solution {
public:
    int change(int amount, vector<int>& coins) {
        // 初始化dp数组,dp[i]表示凑成金额i的组合数
        vector<int> dp(amount + 1, 0);
        dp[0] = 1;  // 凑成金额0的方式有1种(不选任何硬币)

        // 动态规划计算
        for (int coin : coins) {
            for (int i = coin; i <= amount; ++i) {
                dp[i] += dp[i - coin];
            }
        }
        
        return dp[amount];  // 返回凑成金额amount的组合数
    }
};

C++代码解释

  1. 初始化:定义 dp 数组,其中 dp[i] 表示凑成金额 i 的组合数,初始时 dp[0] = 1
  2. 动态规划递推:遍历每个硬币 coin,逐步更新 dp[i],使其表示所有可能的组合数。
  3. 返回结果:最终返回 dp[amount],即凑成 amount 的所有组合数量。

总结

  • 问题核心:这是一个典型的动态规划问题,通过递推的方式解决硬币组合问题。每个硬币可以重复使用,因此每个状态的计算需要考虑之前的状态。
  • 时间复杂度:时间复杂度为 O(amount * n),其中 n 为硬币种类的数量,amount 为目标金额。
  • 空间复杂度:空间复杂度为 O(amount),因为我们只需要维护一个 dp 数组,适合处理中等规模的输入。

您可能感兴趣的与本文相关的镜像

Python3.8

Python3.8

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

ALSKai

今天又能喝柠檬茶啦

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

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

打赏作者

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

抵扣说明:

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

余额充值