/**
* @author zyw
* @date 2023/6/27 10:23
* @describe 给定两个长度都为N的数组weights和values,
* weights[i]和values[i]分别代表 i号物品的重量和价值。
* 给定一个正数bag,表示一个载重bag的袋子,
* 你装的物品不能超过这个重量。
* 返回你能装下最多的价值是多少?
**/
public class knapsack {
// 暴力递归
public static int merit(int[] weights, int[] values, int bag) {
return process(weights, values, 0, bag);
}
/**
* @param weights 重量
* @param values 价值
* @param index 货物的索引
* @param bag 背包重量
* @return 最大价值
*/
private static int process(int[] weights, int[] values, int index, int bag) {
if (bag < 0) {
return -1;
}
if (index == weights.length) {
return 0;
}
// 不要这个货物
int p1 = process(weights, values, index + 1, bag);
// 要这个货物
int p2 = 0;
int value = process(weights, values, index + 1, bag - weights[index]);
if (value != -1) {
p2 = values[index] + value;
}
return Math.max(p1, p2);
}
// 缓存
public static int merit2(int[] weights, int[] values, int bag) {
int[][] dp = new int[weights.length + 1][bag + 1];
for (int i = 0; i < weights.length + 1; i++) {
for (int j = 0; j < bag + 1 ; j++) {
dp[i][j] = -1;
}
}
return process2(weights, values, 0, bag,dp);
}
private static int process2(int[] weights, int[] values, int index, int bag, int[][] dp) {
int ans;
if(bag<0){
return -1;
}else if(index==weights.length){
ans = 0;
}else {
if(dp[index][bag]!=-1){
return dp[index][bag];
}
int p1 = process2(weights,values,index + 1,bag,dp);
int p2 = 0;
int i = process2(weights, values, index + 1, bag - weights[index], dp);
if(i!=-1){
p2 = values[index] + i;
}
ans = Math.max(p1,p2);
}
dp[index][bag] = ans;
return ans;
}
// int[] weights = {1, 3, 6, 2};
// int[] values = {2, 1, 10, 3};
// int bagWeight = 10;
// 0 10
// 1 9 1 10
// 2 6 2 9 2 7 2 10
// 3 0 3 6 3 9 3 3 3 7 3 1 3 10 3 4
// 4 -1 4 6 4 6 4 4 4 7 4 9 4 1 4 3 4 5 4 7 4 -1 4 1 4 8 4 10 4 2 4 4
// 根据上方排列可看出有重复的子过程
// 0 1 2 3 4 物品索引
// 0 ¥ & 0
// 1 0
// 2 0
// 3 0
// 4 0
// 5 0
// 6 0
// 7 0
// 8 & 0
// 9 0
// 10 * 0
// 11 0
// 背包重量
// ¥ 位置依赖于 & 这两个 位置 那么 物品索引 3 可以依赖物品索引 4 的位置 从右往左按照序列补全此表即可
// 最终版本
public static int merit3(int[] weights, int[] values, int bag) {
int[][] dp = new int[weights.length + 1][bag + 1];
for (int i = weights.length-1; i >=0 ; i--) {
for (int j = 0; j <= bag; j++) {
int p1 = dp[i + 1][j];
int p2 = 0;
int value = j - weights[i] < 0 ? -1 : dp[i + 1][j - weights[i]];
if (value != -1) {
p2 = values[i] + value;
}
dp[i][j] = Math.max(p1, p2);
}
}
return dp[0][bag];
}
// 空间压缩
public static int merit4(int[] weights, int[] values, int bag) {
if(weights==null||values==null||weights.length==0||values.length==0||weights.length!=values.length){
return 0;
}
int[] dp = new int[bag + 1];
// 从最后一个物品开始遍历
for (int i = weights.length - 1; i >= 0; i--) {
for (int j = bag; j >= 0; j--) {
int p1 = dp[j];
int p2 = 0;
// 如果当前背包容量减去当前物品的重量小于0,表示当前物品无法放入背包
// 否则,计算将当前物品放入背包后的总价值
int value = j - weights[i] < 0 ? -1 : dp[j - weights[i]];
if (value != -1) {
p2 = values[i] + value;
}
// 取当前状态和将当前物品放入背包后的状态中的较大值作为当前背包容量下的最大价值
dp[j] = Math.max(p1, p2);
}
}
return dp[bag];
}
public static void main(String[] args) {
int[] weights = {1, 3, 6, 2};
int[] values = {2, 1, 10, 3};
// 0 10
// 1 9 1 10
// 2 6 2 9 2 7 2 10
// 3 0 3 6 3 9 3 3 3 7 3 1 3 10 3 4
// 4 -1 4 6 4 6 4 4 4 7 4 9 4 1 4 3 4 5 4 7 4 -1 4 1 4 8 4 10 4 2 4 4
int bagWeight = 10;
System.out.println(merit(weights, values, bagWeight));
System.out.println(merit2(weights, values, bagWeight));
System.out.println(merit3(weights, values, bagWeight));
System.out.println(merit4(weights, values, bagWeight));
}
}
背包问题--暴力递归转动态规划3
最新推荐文章于 2024-07-13 13:36:16 发布