LeetCode 热题 100_零钱兑换(85_322_中等_C++)(动态规划)

发布于:2025-04-11 ⋅ 阅读:(25) ⋅ 点赞:(0)

题目描述:

给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。

计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。

你可以认为每种硬币的数量是无限的。

输入输出样例:

示例 1:
输入:coins = [1, 2, 5], amount = 11
输出:3
解释:11 = 5 + 5 + 1

示例 2:
输入:coins = [2], amount = 3
输出:-1

示例 3:
输入:coins = [1], amount = 0
输出:0

提示:
1 <= coins.length <= 12
1 <= coins[i] <= 231 - 1
0 <= amount <= 104

题解:

解题思路:

思路一(动态规划):

1、通过题目的描述需解决凑成总金额所需的最少的硬币个数,且硬币可以重复使用,这样自然而然将问题转换为了完全背包问题(金币的数量为物品的个数,金额为物品的价值)。

2、具体思路如下:
① dp[j],dp数组的含义为:放满j容量的背包,最少的物品数量。
② dp[j]=min(dp[j],dp[j-nums[i]]+1) ,等号右侧 dp[ j ]代表不放物品 i , dp[j-nums[i]]+1 代表放物品 j 。
③ dp[0]=0,因容量为 0 的背包可以放 0 件物品
④ 因取最小值,其他的dp数组的元素值为INT_MAX,数以这里需考虑溢出的情况dp[j-nums[i]]若为INT_MAX再加一则会溢出
⑤ 因可重复使用同一金额的硬币则遍历顺序为从左到右。

3、复杂度分析:
① 时间复杂度:O(Sn),其中 S 是金额,n 是面额数(物品的价值)。我们一共需要计算 O(S) 个状态,S 为题目所给的总金额(背包容量)。双重循环遍历 物品 和 背包。

② 空间复杂度:O(S)。dp 所需的空间 。

代码实现

代码实现(思路一(动态规划)):

class Solution{
public:
    // 定义 coinChange 函数,参数为硬币面值数组 coins 和目标金额 amount
    int coinChange(vector<int> &coins, int amount){
        // 初始化 dp 数组,大小为 amount + 1(从 0 到 amount),并将所有元素初始化为 INT_MAX
        // dp[i] 表示凑成金额 i 所需要的最少硬币数量
        vector<int> dp(amount + 1, INT_MAX);

        // dp[0] = 0,因为凑成金额 0 不需要任何硬币
        dp[0] = 0;

        // 外层循环遍历每种硬币(物品)
        for (int i = 0; i < coins.size(); i++){
            // 内层循环遍历从当前硬币面值到目标金额之间的所有金额(背包)
            for (int j = coins[i]; j <= amount; j++){
                // 防止数据溢出:只有当 dp[j - coins[i]] 不等于 INT_MAX 时,才进行更新
                // 这是因为如果 dp[j - coins[i]] 为 INT_MAX,表示无法凑成金额 j - coins[i]
                if (dp[j - coins[i]] != INT_MAX) {
                    // 更新 dp[j]:表示使用当前硬币后,凑成金额 j 的最小硬币数
                    dp[j] = min(dp[j], dp[j - coins[i]] + 1);
                }
            }
        }
        
        // 如果 dp[amount] 还是 INT_MAX,表示无法凑成目标金额,返回 -1
        // 否则返回 dp[amount],即凑成目标金额所需要的最少硬币数量
        return dp[amount] == INT_MAX ? -1 : dp[amount];
    }
};

以思路一为例进行调试
#include<iostream>
#include<vector>
using namespace std;

class Solution{
public:
    // 定义 coinChange 函数,参数为硬币面值数组 coins 和目标金额 amount
    int coinChange(vector<int> &coins, int amount){
        // 初始化 dp 数组,大小为 amount + 1(从 0 到 amount),并将所有元素初始化为 INT_MAX
        // dp[i] 表示凑成金额 i 所需要的最少硬币数量
        vector<int> dp(amount + 1, INT_MAX);

        // dp[0] = 0,因为凑成金额 0 不需要任何硬币
        dp[0] = 0;

        // 外层循环遍历每种硬币(物品)
        for (int i = 0; i < coins.size(); i++){
            // 内层循环遍历从当前硬币面值到目标金额之间的所有金额(背包)
            for (int j = coins[i]; j <= amount; j++){
                // 防止数据溢出:只有当 dp[j - coins[i]] 不等于 INT_MAX 时,才进行更新
                // 这是因为如果 dp[j - coins[i]] 为 INT_MAX,表示无法凑成金额 j - coins[i]
                if (dp[j - coins[i]] != INT_MAX) {
                    // 更新 dp[j]:表示使用当前硬币后,凑成金额 j 的最小硬币数
                    dp[j] = min(dp[j], dp[j - coins[i]] + 1);
                }
            }
        }
        
        // 如果 dp[amount] 还是 INT_MAX,表示无法凑成目标金额,返回 -1
        // 否则返回 dp[amount],即凑成目标金额所需要的最少硬币数量
        return dp[amount] == INT_MAX ? -1 : dp[amount];
    }
};

int main(int argc, char const *argv[])
{
    // 给定硬币面值数组和目标金额
    vector<int> coins = {1, 2, 5};
    int amount = 11;

    // 创建 Solution 对象
    Solution s;

    // 调用 coinChange 函数并输出结果
    cout << s.coinChange(coins, amount);
    return 0;
}

LeetCode 热题 100_零钱兑换(85_322)原题链接
欢迎大家和我沟通交流(✿◠‿◠)