代码随想录算法训练营第四十二天| 01背包问题,你该了解这些!, 01背包问题,你该了解这些! 滚动数组, 416. 分割等和子集

 题目与题解

01背包问题,你该了解这些!

题目链接:01背包问题,你该了解这些!

代码随想录题解:01背包问题,你该了解这些!

视频讲解:带你学透0-1背包问题!| 关于背包问题,你不清楚的地方,这里都讲了!| 动态规划经典问题 | 数据结构与算法_哔哩哔哩_bilibili

解题思路:

        直接看答案

看完代码随想录之后的想法       

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

对于背包问题,有一种写法, 是使用二维数组,即dp[i][j] 表示从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少

2. 确定递推公式

再回顾一下dp[i][j]的含义:从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少。

那么可以有两个方向推出来dp[i][j],

  • 不放物品i:由dp[i - 1][j]推出,即背包容量为j,里面不放物品i的最大价值,此时dp[i][j]就是dp[i - 1][j]。(其实就是当物品i的重量大于背包j的重量时,物品i无法放进背包中,所以背包内的价值依然和前面相同。)
  • 放物品i:由dp[i - 1][j - weight[i]]推出,dp[i - 1][j - weight[i]] 为背包容量为j - weight[i]的时候不放物品i的最大价值,那么dp[i - 1][j - weight[i]] + value[i] (物品i的价值),就是背包放物品i得到的最大价值

所以递归公式: dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);

3. dp数组如何初始化

首先从dp[i][j]的定义出发,如果背包容量j为0的话,即dp[i][0],无论是选取哪些物品,背包价值总和一定为0

状态转移方程 dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]); 可以看出i 是由 i-1 推导出来,那么i为0的时候就一定要初始化。

dp[0][j],即:i为0,存放编号0的物品的时候,各个容量的背包所能存放的最大价值。

那么很明显当 j < weight[0]的时候,dp[0][j] 应该是 0,因为背包容量比编号0的物品重量还小。

当j >= weight[0]时,dp[0][j] 应该是value[0],因为背包容量放足够放编号0物品。

4. 遍历顺序:按行(先遍历物品)或按列(先遍历背包重量)都可以

5. 举例

import java.util.*;
public class ID46Kama {
    public static void main (String[] args) {
        /* code */
        Scanner scanner = new Scanner(System.in);
        int m = scanner.nextInt();
        int n = scanner.nextInt();
        int[] weight = new int[m];
        int[] value = new int[m];
        for(int i = 0; i < m; i++) {
            weight[i] = scanner.nextInt();
        }
        for(int i = 0; i < m; i++) {
            value[i] = scanner.nextInt();
        }
        // 二维dp
        int[][] dp = new int[m][n+1];
        for (int j = 0; j <= n; j++) {
            if (weight[0] <= j )
                dp[0][j] = value[0];
        }
        for(int i = 1; i < m; i++) {
            for(int j = 1; j <= n; j++) {
                if (j - weight[i] >= 0) {
                    dp[i][j] = Math.max(dp[i-1][j], dp[i-1][j - weight[i]]+value[i]);
                } else {
                    dp[i][j] = dp[i-1][j];
                }
            }
        }
        System.out.println(dp[m-1][n]);
    }
}

遇到的困难

        Scanner用法又不太记得了,用ACM模式写很容易写错,各种小错误,真难

01背包问题,你该了解这些! 滚动数组

题目链接:​​​​​​​01背包问题,你该了解这些! 滚动数组

代码随想录题解:01背包问题,你该了解这些! 滚动数组

视频讲解:带你学透01背包问题(滚动数组篇) | 从此对背包问题不再迷茫!_哔哩哔哩_bilibili

解题思路:

        看答案

看完代码随想录之后的想法 

        二维数组可以压缩为一维数组,如果把dp[i - 1]那一层拷贝到dp[i]上,表达式完全可以是:dp[i][j] = max(dp[i][j], dp[i][j - weight[i]] + value[i]);

1. 确定dp数组的定义

在一维dp数组中,dp[j]表示:容量为j的背包,所背的物品价值可以最大为dp[j]。

2. 一维dp数组的递推公式

dp[j]有两个选择,一个是取自己dp[j] 相当于 二维dp数组中的dp[i-1][j],即不放物品i,一个是取dp[j - weight[i]] + value[i],即放物品i,指定是取最大的,毕竟是求最大价值,

所以递归公式为:

dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);

可以看出相对于二维dp数组的写法,就是把dp[i][j]中i的维度去掉了。

3. 一维dp数组如何初始化

dp[j]表示:容量为j的背包,所背的物品价值可以最大为dp[j],那么dp[0]就应该是0,因为背包容量为0所背的物品的最大价值就是0。

dp数组在推导的时候一定是取价值最大的数,如果题目给的价值都是正整数那么非0下标都初始化为0就可以了。

假设物品价值都是大于0的,所以dp数组初始化的时候,都初始为0就可以了

4. 一维dp数组遍历顺序

二维dp遍历的时候,背包容量是从小到大,而一维dp遍历的时候,背包是从大到小。并且只能从物品开始遍历,不能从背包重量遍历。这里看不太懂,后续再说吧

5. 举例

public class ID46Kama {
    public static void main (String[] args) {
        /* code */
        Scanner scanner = new Scanner(System.in);
        int m = scanner.nextInt();
        int n = scanner.nextInt();
        int[] weight = new int[m];
        int[] value = new int[m];
        for(int i = 0; i < m; i++) {
            weight[i] = scanner.nextInt();
        }
        for(int i = 0; i < m; i++) {
            value[i] = scanner.nextInt();
        }
        // 一维dp
        int[] dp1 = new int[n+1];
        for (int i = 1; i < m; i++) {
            for (int j = n; j >= weight[i]; j++) {
                dp1[j] = Math.max(dp1[j], dp1[j - weight[i]]+value[i]);
            }
        }
        System.out.println(dp1[n]);
    }
}

遇到的困难

        着实不太看得懂遍历顺序,感觉是为了避免用上一行计算当前行的时候,出现用当前行的结果计算当前行的情况。

416. 分割等和子集

题目链接:​​​​​​​416. 分割等和子集

代码随想录题解:416. 分割等和子集

视频讲解:动态规划之背包问题,这个包能装满吗?| LeetCode:416.分割等和子集_哔哩哔哩_bilibili

解题思路:

        乍一看感觉就是组合问题,可以用回溯做。

        首先对输入数组求和,然后判断和是不是偶数。如果是的话,题目就转换成求数组中是否存在一个组合,其和为sum/2;否则直接return false。

        但是随想录说回溯会超时,而且这里要求用背包做,就没有写。

看完代码随想录之后的想法 

只有确定了如下四点,才能把01背包问题套到本题上来。

  • 背包的体积为sum / 2
  • 背包要放入的商品(集合里的元素)重量为 元素的数值,价值也为元素的数值
  • 背包如果正好装满,说明找到了总和为 sum / 2 的子集。
  • 背包中每一个元素是不可重复放入

        应用题转换为数学题:背包大小target为sum/2,每个数价值为nums[i],重量也为nums[i],求背包中是否存在dp[target]=target的情况。

class Solution {
    public boolean canPartition(int[] nums) {
		int sum = 0;
		for (int i = 0; i < nums.length; i++) {
			sum += nums[i];
		}
		if (sum % 2 == 1) return false;
		int target = sum/2;
		int[] dp = new int[target + 1];
		for (int i = 0; i < nums.length; i++) {
			for (int j = target; j >= nums[i]; j--) {
				dp[j] = Math.max(dp[j], dp[j-nums[i]] + nums[i]);
			}
		}
		return dp[target] == target;
    }
}

也可以剪枝一下,在循环内提前判断dp[target] == target是否成立。

遇到的困难

        这道题无论如何都想不到要用背包的,真的很难想。

今日收获

        学习了一下01背包问题的求解方法,只能说啥也不会,全都靠背。

代码随想录算法训练营是一个优质的学习和讨论平台,提供了丰富的算法训练内容和讨论交流机会。在训练营中,学员们可以通过观看视频讲解来学习算法知识,并根据讲解内容进行刷题练习。此外,训练营还提供了刷题建议,例如先看视频、了解自己所使用的编程语言、使用日志等方法来提高刷题效果和语言掌握程度。 训练营中的讨论内容非常丰富,涵盖了各种算法知识点和解题方法。例如,在第14训练营中,讲解了二叉树的理论基础、递归遍历、迭代遍历和统一遍历的内容。此外,在讨论中还分享了相关的博客文章和配图,帮助学员更好地理解和掌握二叉树的遍历方法。 训练营还提供了每日的讨论知识点,例如在第15的讨论中,介绍了层序遍历的方法和使用队列来模拟一层一层遍历的效果。在第16的讨论中,重点讨论了如何进行调试(debug)的方法,认为掌握调试技巧可以帮助学员更好地解决问题和写出正确的算法代码。 总之,代码随想录算法训练营是一个提供优质学习和讨论环境的平台,可以帮助学员系统地学习算法知识,并提供了丰富的讨论内容和刷题建议来提高算法编程能力。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [代码随想录算法训练营每日精华](https://blog.csdn.net/weixin_38556197/article/details/128462133)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值