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

完全背包理论基础

  • 纯背包问题的特点:每个物品可以无限次拿

  • 与0-1背包唯一不同:

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

    0-1背包不可以添加多次,需要从大到小去遍历

    2023-04-27T18_52_54

    Snipaste_2023-04-27_18-49-17
  • 对于纯完全背包问题,其for循环的先后循环是可以颠倒的
    但如果题目稍稍有点变化,就会体现在遍历顺序上。
    如果问装满背包有几种方式的话? 那么两个for循环的先后顺序就有很大区别了,而leetcode上的题目都是这种稍有变化的类型。

  • 先遍历背包的话,注意背包的索引要从0开始(注意)

//先遍历物品,再遍历背包
private static void testCompletePack(){
    int[] weight = {1, 3, 4};
    int[] value = {15, 20, 30};
    int bagWeight = 4;
    int[] dp = new int[bagWeight + 1];
    for (int i = 0; i < weight.length; i++){ // 遍历物品
        for (int j = weight[i]; j <= bagWeight; j++){ // 遍历背包容量
            dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]);
        }
    }
    for (int maxValue : dp){
        System.out.println(maxValue + "   ");
    }
}

//先遍历背包,再遍历物品
private static void testCompletePackAnotherWay(){
    int[] weight = {1, 3, 4};
    int[] value = {15, 20, 30};
    int bagWeight = 4;
    int[] dp = new int[bagWeight + 1];
    for (int i = 1; i <= bagWeight; i++){ // 遍历背包容量
        for (int j = 0; j < weight.length; j++){ // 遍历物品
            if (i - weight[j] >= 0){
                dp[i] = Math.max(dp[i], dp[i - weight[j]] + value[j]);
            }
        }
    }
    for (int maxValue : dp){
        System.out.println(maxValue + "   ");
    }
}

518. 零钱兑换 II

与目标和的题目很相似,就是求装满容量的组合数

本题物品的容量和价值的含义一样的

本题学到东西:①组合和排列问题不同遍历顺序②背包问题变型思路

  • 题目链接:代码随想录

  • 背包问题变型思路

    纯完全背包是凑成背包最大价值是多少,而本题是要求凑成总金额的物品组合个数

    递推公式不同于dp[j] = max(dp[j],dp[j - weight[i]] + value[i]),而是dp[j] += dp[j - coins[i]],-这是一个求装满容器有多少种方法的模板

  • 解题思路:
    1.dp[j]:凑成总金额j的货币组合数为dp[j]
    2.dp[j] += dp[j - coins[i]];
    3.dp[0]一定要为1。如果dp[0] = 0 的话,后面所有推导出来的值都是0了。
    4.遍历顺序:一定要先遍历背包

    2023-04-27T19_48_05

  • 推导过程:

    Snipaste_2023-04-27_19-49-07

class Solution {
    public int change(int amount, int[] coins) {

        //dp[i]表示装满容量为i的背包有几种方法
        //i代表容量
        int[] dp = new int[amount + 1];//定义背包

        //2.初始化
        dp[0] = 1;

        //3.遍历
        for (int i = 0; i < coins.length; i++) {//先遍历物品

            for (int j = coins[i]; j <= amount; j++) { //遍历背包
                dp[j] += dp[j - coins[i]];
            }
        }

        return dp[amount];

    }   
}

377. 组合总和 Ⅳ

跟上一题对比来看

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

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

  • 解题思路:
    注意遍历顺序即可,其他思路同上。
    ①先遍历背包再遍历物品,遍历物品的时候要从头开始遍历,可以想象成是竖着遍历
    ②在判断当前背包能装下物品时,对dp数组进行修改时,索引为i表示当前背包,因为dp[i]表示为装满重量为i的背包,能有几种组合

  • 推导过程:

    image-20230427203542459

public int combinationSum4(int[] nums, int target) {

    //1.dp[i]表示为装满重量为i的背包,能有几种组合
    int[] dp = new int[target + 1];

    //2.初始化
    dp[0] = 1;

    for (int i = 0; i <= target; i++) {//先遍历背包

        for (int j = 0; j < nums.length; j++) {//再遍历物品,j要从nums[0]开始,因为每一个物品都有可能装进背包和 先遍历物品的情况区别开来

            if(i >= nums[j]){
                dp[i] += dp[i - nums[j]];//这里dp[i]的索引不能错,因为是同一个i表示背包
            }
        }
    }

    return dp[target];

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值