动态规划—518. 零钱兑换 II
题目描述

前言
零钱兑换 II 是一个经典的动态规划问题。给定几种不同面额的硬币和一个总金额 amount,要求找出可以凑成总金额的不同硬币组合的数量。每种硬币可以使用无限次。该问题与常见的 背包问题 相似,但不同的是这里的每种硬币可以重复使用。
基本思路
1. 问题定义
给定一个整数数组 coins 表示不同面额的硬币,以及一个整数 amount,表示需要凑成的金额。要求计算出能够凑成总金额 amount 的不同组合的数量。
举例:
- 输入:
coins = [1, 2, 5],amount = 5 - 输出:
4 - 解释:有 4 种方式可以凑成 5:
5 = 55 = 2 + 2 + 15 = 2 + 1 + 1 + 15 = 1 + 1 + 1 + 1 + 1
2. 理解问题和递推关系
动态规划思想:
我们可以通过动态规划的方法解决该问题。对于每一个硬币,我们依次考虑它是否可以加入到组合中,并且每个硬币可以使用多次。我们定义 dp[i] 表示凑成金额 i 的组合数。
状态定义:
dp[i]表示凑成金额i的组合数。
状态转移方程:
- 对于每个硬币
coin,遍历从coin到amount的所有金额i,并更新dp[i]:
d p [ i ] + = d p [ i − c o i n ] dp[i] += dp[i - coin] dp[i]+=dp[i−coin]dp[i]是凑成金额i的组合数。dp[i - coin]表示当前金额减去硬币面值后的剩余金额可以凑成的组合数。
边界条件:
dp[0] = 1,表示凑成金额 0 有 1 种方式(即不选任何硬币)。
动态规划思路:
- 初始化
dp数组,dp[0] = 1。 - 对于每个硬币
coin,更新dp数组中的所有元素。 - 最终结果为
dp[amount]。
3. 解决方法
动态规划方法
- 初始化:创建一个大小为
amount + 1的dp数组,dp[0] = 1表示凑成金额 0 的方式为 1 种(即不使用任何硬币)。 - 状态转移:遍历每个硬币,对于每个硬币,从金额
coin开始,逐步更新dp[i],即通过递推公式dp[i] += dp[i - coin]。 - 返回结果:
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 + 1的dp数组。
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代码解释
- 初始化:定义
dp数组,其中dp[i]表示凑成金额i的组合数,初始时dp[0] = 1。 - 动态规划递推:通过遍历每个硬币
coin,逐步更新dp[i],使其表示所有可能的组合数。 - 返回结果:最终返回
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++代码解释
- 初始化:定义
dp数组,其中dp[i]表示凑成金额i的组合数,初始时dp[0] = 1。 - 动态规划递推:遍历每个硬币
coin,逐步更新dp[i],使其表示所有可能的组合数。 - 返回结果:最终返回
dp[amount],即凑成amount的所有组合数量。
总结
- 问题核心:这是一个典型的动态规划问题,通过递推的方式解决硬币组合问题。每个硬币可以重复使用,因此每个状态的计算需要考虑之前的状态。
- 时间复杂度:时间复杂度为
O(amount * n),其中n为硬币种类的数量,amount为目标金额。 - 空间复杂度:空间复杂度为
O(amount),因为我们只需要维护一个dp数组,适合处理中等规模的输入。

567

被折叠的 条评论
为什么被折叠?



