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