1. LeetCode 01背包问题 二维
题目链接:https://kamacoder.com/problempage.php?pid=1046
文章链接:https://programmercarl.com/背包理论基础01背包-1.html#算法公开课
视频链接:https://www.bilibili.com/video/BV1cg411g7Y6/
思路:
01背包问题
- 定义dp数组 dp[i][j]表示在0~i物品内任意选择,放入j空间大小的背包中的最大价值
int[][] dp = new int[M][N+1];- 递推公式
dp[i][j] =
Math.max(dp[i-1][j],dp[i-1][j-Integer.valueOf(space[i])]+Integer.valueOf(value[i]))- 初始化
for (int j=Integer.valueOf(space[0]);j<=N;j++) {
dp[0][j] = Integer.valueOf(value[0]);
}- 遍历顺序 先物品再空间
注意:
1️⃣若当前背包的容量都没有当前物品i大的时候,是不放物品i的。那么前i-1个物品能放下的最大价值就是当前情况的最大价值;
2️⃣若当前背包的容量可以放下物品i
那么此时分两种情况:
1、不放物品i
2、放物品i
比较这两种情况下,哪种背包中物品的最大价值最大
import java.util.*;// 01背包问题
public class Main {public static void main(String[] args) {Scanner sc = new Scanner(System.in);String[] firLine = sc.nextLine().split(" ");String[] space = sc.nextLine().split(" ");String[] value = sc.nextLine().split(" ");int M = Integer.valueOf(firLine[0]); // 物品数量int N = Integer.valueOf(firLine[1]); // 背包空间// 1. 定义dp数组 dp[i][j]表示在0~i物品内选择j空间的最大价值int[][] dp = new int[M][N+1];// 2. 递推公式// dp[i][j] = Math.max(dp[i-1][j],// dp[i-1][j-Integer.valueOf(space[i])]+Integer.valueOf(value[i]))// 3. 初始化for (int j=Integer.valueOf(space[0]);j<=N;j++) {dp[0][j] = Integer.valueOf(value[0]);}// 4. 遍历顺序 先物品再空间for (int i=1;i<M;i++) {for (int j=0;j<N+1;j++) {if (j < Integer.valueOf(space[i])) {dp[i][j] = dp[i-1][j];} else {dp[i][j] = Math.max(dp[i-1][j],dp[i-1][j-Integer.valueOf(space[i])]+Integer.valueOf(value[i]));}}}System.out.println(dp[M-1][N]);}
}
2. LeetCode 01背包问题 一维
题目链接:https://kamacoder.com/problempage.php?pid=1046
文章链接:https://programmercarl.com/背包理论基础01背包-2.html#思路
视频链接:https://www.bilibili.com/video/BV1BU4y177kY/
思路:
01背包问题
- 确定dp数组的定义
在一维dp数组中,dp[j]表示:容量为j的背包,所背的物品价值可以最大为dp[j]。- 一维dp数组的递推公式
dp[j]有两个选择:
(1)不放物品i:取自己dp[j],相当于 二维dp数组中的dp[i-1][j];
(2)放物品i:取dp[j - weight[i]] + value[i]。
dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);- 一维dp数组如何初始化
关于初始化,一定要和dp数组的定义吻合,否则到递推公式的时候就会越来越乱。dp[0]应该是0,因为背包容量为0所背的物品的最大价值就是0。
除了下标0的位置,假设物品价值都是大于0的,所以dp数组初始化的时候,都初始为0就可以了。- 一维dp数组遍历顺序
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]);}// j>=weight[i],即物品i只能放到weight大于该物品重量的背包中。
}// 归根结底,当前物品只可能放入背包重量大于当前物品重量的背包中
import java.util.*;// // 01背包问题
// // 二维数组
// public class Main {
// public static void main(String[] args) {
// Scanner sc = new Scanner(System.in);
// String[] firLine = sc.nextLine().split(" ");
// String[] space = sc.nextLine().split(" ");
// String[] value = sc.nextLine().split(" ");
// int M = Integer.valueOf(firLine[0]); // 物品数量
// int N = Integer.valueOf(firLine[1]); // 背包空间// // 1. 定义dp数组 dp[i][j]表示在0~i物品内选择j空间的最大价值
// int[][] dp = new int[M][N+1];// // 2. 递推公式
// // dp[i][j] = Math.max(dp[i-1][j],
// // dp[i-1][j-Integer.valueOf(space[i])]+Integer.valueOf(value[i]))// // 3. 初始化
// for (int j=Integer.valueOf(space[0]);j<=N;j++) {
// dp[0][j] = Integer.valueOf(value[0]);
// }// // 4. 遍历顺序 先物品再空间
// for (int i=1;i<M;i++) {
// for (int j=0;j<N+1;j++) {
// if (j < Integer.valueOf(space[i])) {
// dp[i][j] = dp[i-1][j];
// } else {
// dp[i][j] = Math.max(dp[i-1][j],
// dp[i-1][j-Integer.valueOf(space[i])]+Integer.valueOf(value[i]));
// }
// }
// }// System.out.println(dp[M-1][N]);// }
// }// 一维数组public class Main {public static void main(String[] args) {Scanner sc = new Scanner(System.in);String[] firLine = sc.nextLine().split(" ");String[] space = sc.nextLine().split(" ");String[] value = sc.nextLine().split(" ");int M = Integer.valueOf(firLine[0]); // 物品数量int N = Integer.valueOf(firLine[1]); // 背包空间// 1. 定义dp数组 dp[j]表示空间j的背包的最大价值int[] dp = new int[N+1];// 2. 递推公式// dp[j] = Math.max(dp[j],dp[j-Integer.valueOf(space[i])]+Integer.valueOf(value[i]))// 3. 初始化// 4. 遍历顺序 先物品再空间for (int i=0;i<M;i++) {for (int j=N;j>=Integer.valueOf(space[i]);j--) {dp[j] = Math.max(dp[j],dp[j-Integer.valueOf(space[i])]+Integer.valueOf(value[i]));}}System.out.println(dp[N]);}
}
3. LeetCode 416. 分割等和子集
题目链接:https://leetcode.cn/problems/partition-equal-subset-sum/description/
文章链接:https://programmercarl.com/0416.分割等和子集.html
视频链接:https://www.bilibili.com/video/BV1rt4y1N7jE/
思路:此题是01背包应用类题。
题目中物品是nums[i],重量是nums[i],价值也是nums[i],背包体积是sum/2。
只有确定了如下四点,才能把01背包问题套到本题上来。
1.背包的体积为sum / 2
2.背包要放入的商品(集合里的元素)重量为 元素的数值,价值也为元素的数值
3.背包如果正好装满,说明找到了总和为 sum / 2 的子集。
4.背包中每一个元素是不可重复放入。
解法:
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 N = sum/2;//1. 定义dp数组 dp[j] 表示 容量j的背包所能获取的最大的物品价值// 注意:这里物品的重量=物品的价值。最大的物品价值=最大的物品重量int[] dp = new int[N+1];//2.递推公式//dp[j] = Math.max(dp[j],dp[j-nums[i]]+nums[i]);//3.初始化//4.遍历顺序 先物品再背包for (int i=0;i<nums.length;i++) {for (int j=N;j>=nums[i];j--) {dp[j] = Math.max(dp[j],dp[j-nums[i]]+nums[i]);}}return dp[N]==N;}
}