算法学习|动态规划 LeetCode 完全背包、 518. 零钱兑换 II 、377. 组合总和 Ⅳ

一、完全背包

完全背包:一件物品可以使用无数次
01背包: 一件物品只能使用一次
纯完全背包:装满背包,所装的最大价值为多少

遍历顺序:正序遍历可以使物品添加多次,先遍历物品再遍历背包(纯完全背包for循环顺序可以颠倒)

// 先遍历物品,在遍历背包
void test_CompletePack() {
    
    
    vector<int> weight = {
    
    1, 3, 4};
    vector<int> value = {
    
    15, 20, 30};
    int bagWeight = 4;
    vector<int> dp(bagWeight + 1, 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;
}
int main() {
    
    
    test_CompletePack();
}

// 先遍历背包,再遍历物品
void test_CompletePack() {
    
    
    vector<int> weight = {
    
    1, 3, 4};
    vector<int> value = {
    
    15, 20, 30};
    int bagWeight = 4;

    vector<int> dp(bagWeight + 1, 0);

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

二、零钱兑换||

给定不同面额的硬币和一个总金额。写出函数来计算可以凑成总金额的硬币组合数。假设每一种面额的硬币有无限个。

思路

装满这个集合(总金额),有多少种方法
1.dp[j]:装满背包容量j有dp[j]方法
2.递推公式:dp[j] += dp[ j - coins[i]]
3.初始化:dp[0] = 1其他非零下标的dp数组初始化为0
4.遍历顺序:先遍历物品再遍历背包只会出现(1,2)(即组合数)
先遍历背包再遍历物品既有(1,2)也有(2,1)(即排列数)

实现代码

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];
    }
};

三、组合总和IV

给定一个由正整数组成且不存在重复数字的数组,找出和为给定目标正整数的组合的个数。

思路

强调元素顺序: 112 和 211 是两个组合

实现代码

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];
    }
};

四、总结

求装满背包有多少种方法:
如果是求组合数,外层for循环遍历物品,内层for循环遍历背包
如果是求排列数,外层for循环遍历背包,内层for循环遍历物品

猜你喜欢

转载自blog.csdn.net/li26324949/article/details/129912508
今日推荐