leetcode322题解

发布时间 2023-11-09 10:39:20作者: 桂洛克船长

今天来解析一下一道中等的leetcode题,题目如下:

给你一个整数数组 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

这其实就是一个背包问题,而背包问题一般有两种解法

  • 先遍历物品,在填充背包
  • 先遍历背包,在用物品填充背包

这里我用的是先物品在背包代码如下:

    int coinChange(vector<int>& coins, int amount) {
        
        vector<int> dp(amount+1,INT_MAX);
        //i表示金额
        //dp[i]表示凑成i金额需要的最少数量

        dp[0]=0;
        for(int i=0;i<coins.size();i++)
        {
            for(int j=coins[i];j<=amount;j++)
            {   
                if(dp[j-coins[i]]!=INT_MAX)
                {
                    // 前面dp值在有计算过的基础上才能转移
                    //dp[j]不装,dp[j-coins[i]]+1装
                    dp[j]=min(dp[j],dp[j-coins[i]]+1);
                }
            }
        }

        return  dp[amount] == INT_MAX ? -1 : dp[amount];
    }

​ 动态规划一般有三个步骤,dp定义、初始化、状态转移。

​ 1.dp定义:dp[i]定义为遍历到i(代表金额)时,所选的硬币能凑成目标金额的最少数量。

​ 2.dp初始化:我们把dp数组设为最大值,方便后续更新。

​ 3.状态转移:dp[j]=min(dp[j],dp[j-coins[i]]+1);这一步也是最难的,需要多做才能想到。

  • 当 j<coins[i]时,装不下,直接继承上一个dp[j]的值
  • 当 j>=coins[i]时,装得下,可以选择装或者不装进行转移,因此装时代码为dp[j-coins[i]]+1;

​ 背包放入coins[i]因此要用当前金额j减去coins[i]得到dp[j-coins[i]]的数量,而加一是因为装了第coins[i]硬币

直接上调试,帮助理解

image-20231109102343840

可以看见第一个循环coins[i]为1块面值,因此递推完一次,凑成所需金额最少硬币数就是金额数。

image-20231109102523624

而当coins[i]面值为2块时,就会用dp[j-coins[i]]来寻找凑成dp[j-coins[i]]所需金额的最少数量硬币,然后加上当前硬币(coins[i])便是金额为j的最少硬币数,到此完全理解递推公式。而if是为了防止整形溢出的因为下面加一可能会溢出,以上就是本题的全部解析。