代码随想录|动态规划|23零钱兑换

leetcode:

题目

思路

(1)装满容量为j的背包,使用的最少硬币数为dp[j]

(2)如果包含coins[i],凑足总额为j - coins[i]的最少个数为dp[j - coins[i]],那么只需要加上一个钱币coins[i]即dp[j - coins[i]] + 1就是dp[j]。

所以dp[j] 要取所有 dp[j - coins[i]] + 1 中最小的。

递推公式:dp[j] = min(dp[j - coins[i]] + 1, dp[j]);

(3)凑足总金额为0所需钱币的个数一定是0,那么dp[0] = 0;

考虑到递推公式的特性,dp[j]必须初始化为一个最大的数,否则就会在min(dp[j - coins[i]] + 1, dp[j])比较的过程中被初始值覆盖。

所以下标非0的元素都是应该是最大值。

(4)本题求钱币最小个数,那么钱币有顺序和没有顺序都可以,都不影响钱币的最小个数

所以本题并不强调集合是组合还是排列。

本题钱币数量可以无限使用,那么是完全背包。所以遍历的内循环是正序

遍历过程中需要加一个判断条件:

if (dp[j - coins[i]] != INT_MAX) { // 如果dp[j - coins[i]]是初始值则跳过
                    dp[j] = min(dp[j - coins[i]] + 1, dp[j]);

 防止dp[j - coins[i]] + 1超出内存限制!

代码如下:

#include <iostream>
#include <vector>
using namespace std;

class Solution
{
public:
    /**
     * 解决零钱兑换问题,计算组成总金额所需的最少硬币数量
     * @param coins 可用的硬币面额数组
     * @param amount 目标总金额
     * @return 组成总金额所需的最少硬币数量,如果无法组成则返回-1
     */
    int coinChange(vector<int> &coins, int amount)
    {
        // 初始化动态规划数组,大小为amount+1,初始值设为INT_MAX表示不可达
        vector<int> dp(amount + 1, INT_MAX);
        // 金额为0时,需要0个硬币
        dp[0] = 0;
        // 遍历每个硬币面额
        for (int i = 0; i < coins.size(); i++)
        {
            // 从当前硬币面额开始,遍历到目标总金额
            for (int j = coins[i]; j <= amount; j++)
            {
                // 如果当前金额减去当前硬币面额后的金额可达(if的作用是防止溢出)
                if (dp[j - coins[i]] != INT_MAX)
                {
                    // 更新dp[j]为当前金额可达的最小硬币数量
                    dp[j] = min(dp[j], dp[j - coins[i]] + 1);
                }
            }
        }
        // 如果目标总金额不可达,返回-1
        if (dp[amount] == INT_MAX)
            return -1;
        // 返回组成总金额所需的最少硬币数量
        return dp[amount];
    }
};

总结

我第一遍写的时候

if (dp[j - coins[i]] != INT_MAX)

这个判断语句没有加,数据会溢出报错。

还有个好方法,就是在初始化dp的时候使用INT_MAX-1。

参考资料

 代码随想录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值