代码随想录算法训练营第44天 |完全背包、518. 零钱兑换 II、377. 组合总和 Ⅳ

TFTree 发布于 2023-03-01 640 次阅读


AI 摘要

这篇文章介绍了完全背包问题在 LeetCode 中的两道经典题目:518. 零钱兑换 II 和 377. 组合总和 IV。对于这两道题,作者详细讲解了确定 dp 数组和下标含义、递推公式、dp 数组如何初始化、遍历顺序以及如何实现,在最后提供了作者的 AC 代码和标准答案。这些内容可以帮助读者更全面、深入地理解完全背包问题的解法和思路。

518. 零钱兑换 II - 力扣(Leetcode)

思路:

  • 这道题是经典完全背包问题,但不是纯完全背包,因为纯完全背包的情况是物品数量无限,求一定背包容量下能容纳的最大价值。而这道题求的是能填满一定背包容量不同的组合数量,且每个物品数量是无限的。与纯完全背包不同的是状态转移方程不同,纯完全背包问题的递推公式是dp[j] = max(dp[j], dp[j - nums[i] + values[i]),而本题的递推公式是dp[j] += dp[j - nums[i]]

    1. 确定dp数组以及下标的含义

      • dp[j]的定义为:凑成金额为j的背包的组合数量
    2. 确定递推公式

      • 状态转移方程为dp[j] += dp[j - coins[i]];
    3. dp数组如何初始化

      vector<int> dp(amount + 1, 0);
    4. 确定遍历顺序

      // 本题采用的是先遍历物品再遍历背包,如果先遍历背包再遍历物品的话[1, 5]和[5, 1]会被认为不同的种类,但本题认为这是同一种。在求排列数量,即把[1, 5]和[5, 1]当作不同的种类时才先遍历背包再遍历物品
      for(int i = 0; i < coins.size(); ++i) {
          for(int j = coins[i]; j <= amount; ++j) {
              dp[j] += dp[j - coins[i]];
          }
      }

我的AC代码

// 时间复杂度O(m x n),空间复杂度O(n)
// m代表amount
class Solution {
public:
    int change(int amount, vector<int>& coins) {
        vector<int> dp(amount + 1, 0);
        dp[0] = 1;
        for(int i = 0; i < coins.size(); ++i) {
            for(int j = coins[i]; j <= amount; ++j) {
                dp[j] += dp[j - coins[i]];
            }
        }
        return dp[amount];
    }
};

标准答案

// 时间复杂度O(m x n),空间复杂度O(n)
// m代表amount 
class Solution {
public:
    int change(int amount, vector<int>& coins) {
        vector<int> dp(amount + 1, 0);
        dp[0] = 1;
        for (int i = 0; i < coins.size(); i++) { // 遍历物品
            for (int j = coins[i]; j <= amount; j++) { // 遍历背包
                dp[j] += dp[j - coins[i]];
            }
        }
        return dp[amount];
    }
};

377. 组合总和 Ⅳ - 力扣(Leetcode)

思路:

  • 本题也是完全背包类问题,因为物品数量无限,所以遍历背包的时候要从小到大遍历,又因为求的是排列数量,所以进行遍历的时候要先遍历背包再遍历物品

    1. 确定dp数组以及下标的含义

      • dp[j]的定义为:target为j的时候所有可能的排列数量
    2. 确定递推公式

      • 状态转移方程为dp[j] += dp[j - nums[i]];
    3. dp数组如何初始化

      vector<int> dp(target + 1, 0);
    4. 确定遍历顺序

      for(int j = 0; j <= target; ++j) {
          for(int i = 0; i < nums.size(); ++i) {
              if (j - nums[i] >= 0 && dp[j] < INT_MAX - dp[j - nums[i]]) {
                  dp[j] += dp[j - nums[i]];
              }
          }
      }

我的AC代码

// 时间复杂度O(n2),空间复杂度O(n)
class Solution {
public:
    int combinationSum4(vector<int>& nums, int target) {
        vector<int> dp(target + 1, 0);
        dp[0] = 1;
        for(int j = 0; j <= target; ++j) {
            for(int i = 0; i < nums.size(); ++i) {
                if (j - nums[i] >= 0 && dp[j] < INT_MAX - dp[j - nums[i]]) {
                    dp[j] += dp[j - nums[i]];
                }
            }
        }
        return dp[target];
    }
};

标准答案

// 时间复杂度O(n2),空间复杂度O(n)
class Solution {
public:
    int combinationSum4(vector<int>& nums, int target) {
        vector<int> dp(target + 1, 0);
        dp[0] = 1;
        for (int i = 0; i <= target; i++) { // 遍历背包
            for (int j = 0; j < nums.size(); j++) { // 遍历物品
                if (i - nums[j] >= 0 && dp[i] < INT_MAX - dp[i - nums[j]]) {
                    dp[i] += dp[i - nums[j]];
                }
            }
        }
        return dp[target];
    }
};