算法学习day44

算法学习day44

  • 1.完全背包
    • 1.1 分析
    • 1.2 代码
  • 2. 力扣 518. 零钱兑换 II
    • 2.1 分析
    • 2.2 代码
  • 3. 力扣 377. 组合总和 Ⅳ
    • 3.1 分析
    • 3.2 代码
  • 4.参考资料

1.完全背包

1.1 分析

有N件物品和一个最多可以背重量为W的背包。第i件物品的重量是weight[i],价值是value[i]。每件物品都有无限个(可以放入背包多次)

01背包的核心代码:

for(int i = 0 ; i < weight.size() ; i ++){       // 遍历物品
    for(int j = bagWeight; j >= weight[i] ; j--){// 遍历背包容量
        dp[j] = max(dp[j] , dp[j- weight[i]]+ value[i]);
    }
}

01背包内嵌循环是从大到小遍历,为了保证每个物品仅被添加一次。

完全背包的物品可以添加多次,所以要从小到大去遍历:

for(int i = 0 ; i < weight.size() ; i++){      // 遍历物品
    for(int j = weight[i]; j<= bagWeight; j++){// 遍历背包容量
        dp[j] = max(dp[j], dp[j - weight[i]]+ value[i]);
    }
}

1.2 代码

// 完全背包问题:每个物品可以无限次选择
void test_CompletePack(){
    vector<int> weight = {1 , 3, 4};     // 物品重量
    vector<int> value = {15 , 20 ,30};   // 物品价值
    int bagWeight = 4;                   // 背包最大容量
    vector<int> dp(bagWeight + 1, 0);    // dp数组,初始化为0
    for(int i = 0 ; i < weight.size() ; i++){       // 遍历物品
        for(int j = weight[i]; j <= bagWeight; j++){// 遍历背包容量
            dp[j] = max(dp[j], dp[j - weight[i]]+ value[i]);  // 完全背包公式
        }
    }
    cout << dp[bagWeight] << endl;      // 输出结果
}

2. 力扣 518. 零钱兑换 II

2.1 分析

动规五部曲
1.确定dp数组以及下标的含义

dp[j] : 凑成总金额j的货币组合数为dp[j]

2.确定递推公式

dp[j] 是所有的dp[j - coins[i]]相加。

递推公式:dp[j] +=dp[j -coins[i]];

3.dp数组如何初始化

dp[0] = 1,如果dp[0] = 0。后续所有的推导出来的值都是0。

4.确定遍历顺序

外层for循环遍历物品,内层for循环遍历背包。

for(int i = 0 ; i < coins.size() ; i++){       // 遍历物品
    for(int j = coins[i] ; j <= amount ; j++){ // 遍历背包容量
        dp[j] += dp[j - coins[i]];
    }
}

5.举例推导dp数组
算法学习day44_第1张图片

2.2 代码

class Solution {
public:
    int change(int amount, vector<int>& coins) {
        // 定义 dp 数组,长度为 amount+1,dp[i] 表示凑出金额 i 的方案数
        vector<int> dp(amount + 1, 0);
        // 初始状态,凑出 0 元有一种方案,即不选择任何硬币
        dp[0] = 1;
        // 遍历硬币
        for (int i = 0; i < coins.size(); i++) {
            // 遍历金额,从 coins[i] 开始是因为比 coins[i] 小的金额无法使用 coins[i] 来凑出
            for (int j = coins[i]; j <= amount; j++) {
                // 转移方程:dp[j] 表示当前凑出金额 j 的方案数,加上选择 coins[i] 后凑出金额 j 的方案数 dp[j - coins[i]]
                dp[j] += dp[j - coins[i]];
            }
        }
        // 返回凑出 amount 的方案数
        return dp[amount];
    }
};

3. 力扣 377. 组合总和 Ⅳ

3.1 分析

动规五部曲
1.确定dp数组以及下标的含义

dp[i] : 凑成目标正整数为i的排列个数为dp[i]

2.确定递推公式

递推公式:dp[i] += dp[i - nums[j]];

3.dp数组如何初始化

由递推公式可知dp[0]需要初始化为1,这样递归其他dp[i]时才会有数值基础。

4.确定遍历顺序

如果求组合数就是外层for循环遍历物品内层for循环遍历背包

如果求排列数就是外层for循环遍历背包内层for循环遍历物品

5.举例来推导dp数组
算法学习day44_第2张图片

3.2 代码

class Solution {
public:
    int combinationSum4(vector<int>& nums, int target) {
        vector<int> dp(target + 1, 0); // dp数组,初始化为0
        dp[0] = 1; // 初始化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]]; // 更新dp[i]
                }
            }
        }
        return dp[target]; // 返回装满背包的方案数
    }
};

4.参考资料

[代码随想录]

你可能感兴趣的:(算法,c++,动态规划,leetcode)