0-1背包理论基础
题目:46. 携带研究材料(第六期模拟笔试) (kamacoder.com)
思路:无
答案
import java.util.*;
public class Main {
public static void main(String[] args) {
// 背包容量 N
// 物品种类 M
Scanner sc = new Scanner(System.in);
int M = sc.nextInt();
int N = sc.nextInt();
int[] values = new int[M];
int[] weights = new int[M];
for(int i = 0; i < M;i++) {
weights[i] = sc.nextInt();
}
for(int i = 0; i < M;i++) {
values[i] = sc.nextInt();
}
int[][] dp = new int[M][N+1];
// 初始化
for(int i = weights[0]; i <= N; i++) {
dp[0][i] = values[0];
}
// 先物品
for(int i = 1; i < M; i++) {
// 后背包
for(int j = 0; j <= N; j++) {
if(weights[i] > j) {
dp[i][j] = dp[i-1][j];
} else {
dp[i][j] = Math.max(dp[i-1][j],dp[i-1][j-weights[i]] + values[i]);
}
}
}
System.out.println(dp[M-1][N]);
}
}
小结
- 注意dp数组的定义,dp[i][j] 表示从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少。
- 初始化dp数组是,应该是new int[M][N+1],N+1才能把容量为N的情况包括进去
- 数组初始化时,全部是0,也就是int[][] dp = new int[M][N+1];之后,dp是0构成的二维数组
- 注意,关键是检查物品重量是否大于当前背包容量,而不是比较总重
0-1背包理论基础
题目:46. 携带研究材料(第六期模拟笔试) (kamacoder.com)
思路:
答案
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner scanner = new Scanner(System.in);
int M=0;
int N=0;
M=scanner.nextInt();
N=scanner.nextInt();
int[] weight=new int[M+1];
int[] value=new int[M+1];
int tmp=0;
for(int i=1;i<=M;i++){
tmp=scanner.nextInt();
weight[i]=tmp;
}
for(int i=1;i<=M;i++){
tmp=scanner.nextInt();
value[i]=tmp;
}
int[] bag=new int[N+1];
for(int i=1;i<=M;i++){
for(int j=N;j>=1;j--){
if(weight[i]<=j){
bag[j]=Math.max(bag[j-weight[i]]+value[i],bag[j]);
}
}
}
System.out.print(bag[N]);
}
}
小结
- 正序遍历用一个物品塞满背包,每次覆盖的数据都是同一个物品塞满的情况,反向遍历就是每次只塞一个,并且比较的数据只来自上一轮,以及当前的value[i]
- 倒序遍历,每次用的是上一次的初始值0,各元素不影响,如果正序遍历,先计算了前面的值,后面元素的计算就不是用上一次的0,会影响到后面的结果
- 一维数组,一定要先遍历物品,再遍历背包
416.分割等和子集
思路:我直接for循环遍历一下,不就行了咩,不行,不是找一个界线,而是把所有元素分成两部分,直接卡在dp数组的定义上,定义dp[i]是取出i个元素与剩余元素对比的所有情况?
答案
class Solution {
public boolean canPartition(int[] nums) {
if(nums == null || nums.length == 0) return false;
int n = nums.length;
int sum = 0;
for(int num : nums) {
sum += num;
}
//总和为奇数,不能平分
if(sum % 2 != 0) return false;
int target = sum / 2;
int[] dp = new int[target + 1];
for(int i = 0; i < n; i++) {
for(int j = target; j >= nums[i]; j--) {
//物品 i 的重量是 nums[i],其价值也是 nums[i]
dp[j] = Math.max(dp[j], dp[j - nums[i]] + nums[i]);
}
//剪枝一下,每一次完成內層的for-loop,立即檢查是否dp[target] == target,優化時間複雜度(26ms -> 20ms)
if(dp[target] == target)
return true;
}
return dp[target] == target;
}
}
小结
- dp[j]表示 背包总容量(所能装的总重量)是j,放进物品后,背的最大重量为dp[j]。
- 妈的,背包大小就是sum/2!