leetcode 322零钱兑换

一、完整代码(精简注释)

复制代码
class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        // dp[i]:凑出金额 i 所需的最少硬币数
        vector<int> dp(amount + 1, amount + 1);
        dp[0] = 0;  // 凑出0元需要0枚硬币

        for (int i = 1; i <= amount; i++) {
            for (int j = 0; j < coins.size(); j++) {
                if (coins[j] <= i) { // 当前硬币不能超过金额i
                    // 选择当前硬币,更新最少硬币数
                    dp[i] = min(dp[i], dp[i - coins[j]] + 1);
                }
            }
        }

        // 无法凑出返回-1,否则返回最少硬币数
        return dp[amount] > amount ? -1 : dp[amount];
    }
};

二、动态规划五步总结

1. 确定 dp 数组含义

dp[i] 表示凑出金额 i 所需要的最少硬币数量。

最终目标是求 dp[amount]。

2. 确定状态转移方程

\ \\boxed{dp\[i=\min(dpi,dpi-coins\[j]+1)} \]

  • dp[i]:当前凑出金额 i 的最少硬币数。

  • dp[i-coins[j]]:凑出剩余金额需要的最少硬币数。

  • +1:加上当前选择的这枚硬币。

  • min():在所有硬币选择中取最优解。

例如 coins = [1,2,5],计算 dp[6]:

复制代码
选择1元:dp[5] + 1 = 2
选择2元:dp[4] + 1 = 3
选择5元:dp[1] + 1 = 2

dp[6] = min(2,3,2) = 2

3. 初始化 dp 数组

复制代码
vector<int> dp(amount + 1, amount + 1);
dp[0] = 0;
  • 第一个 amount+1:数组长度,包含 dp[0] 到 dp[amount]。

  • 第二个 amount+1:初始化为一个不可能的较大值,表示暂时无法凑出。

  • dp[0]=0:凑出金额 0 不需要硬币。

这里用 amount+1 代替 INT_MAX,既能表示无解,又能避免状态转移时整数溢出。

4. 确定遍历顺序

复制代码
for (int i = 1; i <= amount; i++) {
    for (int j = 0; j < coins.size(); j++) {
        if (coins[j] <= i) {
            dp[i] = min(dp[i], dp[i - coins[j]] + 1);
        }
    }
}
  • 外层循环:从小金额到大金额,保证计算当前状态时,之前的小金额已经计算完成。

  • 内层循环:遍历所有硬币,寻找最少硬币数量。

  • coins[j] <= i:防止 i-coins[j] 为负数,导致数组越界。

由于每种硬币可以无限使用,因此同一枚硬币可以在不同状态转移中重复选择。

5. 确定返回值

复制代码
return dp[amount] > amount ? -1 : dp[amount];
  • 如果 dp[amount] > amount,说明无法凑出,返回 -1。

  • 否则返回最少硬币数量。

三、复杂度分析

设 n 为硬币种类数。

  • 时间复杂度: \(O(amount \times n)\),每个金额都需要遍历所有硬币。

  • 空间复杂度: \(O(amount)\),使用一维 dp 数组存储结果。

四、核心思路总结

这道题属于完全背包问题中的求最小值问题,因为每种硬币可以无限次使用。

核心思想是:

要求凑出金额 i 的最少硬币数,就枚举最后选择的硬币,利用剩余金额的最优解,再加上当前这枚硬币。

记住三个关键点:

  1. dp[i] 存储的是凑出金额 i 的最优解,而不是具体硬币组合。

  2. dp[i-coins[j]]+1 表示选择一枚当前硬币后的总数量。

  3. amount+1 作为无解标记,是这道题初始化的关键技巧。

这与之前做的「完全平方数」是同一种动态规划思路,只不过一个枚举硬币面值,一个枚举完全平方数。

相关推荐
海绵宝宝转agent4 小时前
2026-10-9 leetcode100刷题+面经整理
笔记·算法·面试
yi0114 小时前
DAY24: LeetCode 167:两数之和 II|从暴力枚举到左右双指针
笔记·python·算法·leetcode·二分查找·双指针
木井巳4 小时前
【BFS 解决拓扑排序】课程表
java·算法·leetcode·广度优先·宽度优先·推荐算法·图搜索算法
扶风ff5 小时前
练题簿在线免费刷题:会员线下组卷、Word 试卷与成绩导入,课堂检测更方便
java·开发语言·算法·小程序·word
yi0115 小时前
LeetCode 15:三数之和|排序 + 双指针,如何避免重复答案?
笔记·python·算法·leetcode·双指针
-dzk-5 小时前
【多维动态规划】LC 1143.最长公共子序列
算法·动态规划
C++ 老炮儿的技术栈5 小时前
char (*csConnectName)[256]; 和 char csConnectName [256] 有什么区别
java·c语言·开发语言·c++·人工智能·算法·c
Omics Pro5 小时前
全新可重分析!代谢组质谱专用
数据库·人工智能·算法·机器学习·自然语言处理
Nebula_g5 小时前
JavaSE加强:线程Thread(线程安全重点)
java·开发语言·jvm·算法·javase·技术栈