0-1背包问题
什么是0-1背包问题?
对于一种物品,要么装入背包,要么不装。所以对于一种物品的装入状态可以取0和1.我们设物品i的装入状态为xi,xi∈ (0,1),此问题称为0-1背包问题。
问题描述:
给定n种物品和一背包。物品i的重量是wi,其价值为vi,背包的容量为C。问应如何选择装入背包的物品,使得装
入背包中物品的总价值最大?
思路:
根据动态规划解题步骤(问题抽象化、建立模型、寻找约束条件、判断是否满足最优性原理、找大问题与小问题的递推关系式、填表、寻找解组成)找出01背包问题的最优解以及解组成,然后编写代码实现。
动态规划求解背包问题
1 package com.lzp.util.dynamic; 2 3 /** 4 * @Author LZP 5 * @Date 2021/3/21 10:50 6 * @Version 1.0 7 * 8 * 0-1背包问题 9 */ 10 public class Knapsack { 11 12 /** 13 * 物品重量 14 */ 15 private static int[] w = {3, 2, 1}; 16 17 /** 18 * 物品价值 19 */ 20 private static int[] v = {2000, 1500, 800}; 21 22 /** 23 * val[i][j] = k 代表前i个物品装入重量为j的背包中最大价值为k 24 */ 25 private static int[][] val = new int[4][5]; 26 27 private static int[][] choice = new int[4][5]; 28 29 public static void main(String[] args) { 30 // 1、初始化val数组,不过Java中默认的int类型数组的值是0,所以不需要,但是这里为了步骤逻辑就写一下 31 for (int i = 0; i < val.length; i++) { 32 val[i][0] = 0; 33 } 34 for (int i = 0; i < val[0].length; i++) { 35 val[0][i] = 0; 36 } 37 38 // for (int i = 0; i < val.length; i++) { 39 // for (int j = 0; j < val[i].length; j++) { 40 // System.out.printf("%d ", val[i][j]); 41 // } 42 // System.out.println(); 43 // } 44 45 // 2、利用公式给val数组赋值,这里从1个物品和背包重量为1开始考虑 46 /* 47 公式: 48 当w[i] > j时,val[i][j] = val[i - 1][j] 49 当j >= w[i]时,val[i][j] = max{val[i - 1][j], v[i] + val[i - 1][j - w[i]]} 50 */ 51 for (int i = 1; i < val.length; i++) { 52 for (int j = 1; j < val[i].length; j++) { 53 // 由于程序里面,数组中的下标是从0开始,所以这里我们获取第1个物品的重量时,要-1 54 if (w[i - 1] > j) { 55 /* 56 第i件物品的重量比此时背包的最大重量还大,所以放不进去,只能将前i - 1个物品放入背包重 57 量为j是的最大价值赋值给此时前i个物品放入背包重量为j,作为此时的最大价值 58 */ 59 val[i][j] = val[i - 1][j]; 60 } else { 61 /* 62 进入else,表示此时第i件物品的重量不超过此时背包的重量,即可以尝试放入,然后将最后产生的最大价值 63 于之前产生的最大价值相比较,选出最大作为此时的最大价值 64 65 注意:这里如果想要在最终的结果获取到底选了哪些物品放入背包中,就需要用一个二维数组去保存记录每次 66 最优的选择 67 */ 68 // 此时这里就不能直接取val[i][j] = max{val[i - 1][j], v[i] + val[i - 1][j - w[i]]}的最大值了 69 // 需要利用if-else判断 70 if (val[i - 1][j] < v[i - 1] + val[i - 1][j - w[i - 1]]) { 71 // 如果最优就用1,标志,默认为0 72 choice[i][j] = 1; 73 val[i][j] = v[i - 1] + val[i - 1][j - w[i - 1]]; 74 } else { 75 val[i][j] = val[i - 1][j]; 76 } 77 } 78 } 79 } 80 81 for (int i = 0; i < val.length; i++) { 82 for (int j = 0; j < val[i].length; j++) { 83 System.out.printf("%4d\t", val[i][j]); 84 } 85 System.out.println(); 86 } 87 88 // 打印选的物品,发现这样会打印出来很多冗余数据 89 /* 90 选了第1件物品 91 选了第1件物品 92 选了第2件物品 93 选了第3件物品 94 选了第3件物品 95 选了第3件物品 96 */ 97 // for (int i = 0; i < choice.length; i++) { 98 // for (int j = 0; j < choice[i].length; j++) { 99 // if (choice[i][j] == 1) { 100 // System.out.printf("选了第%d件物品\n", i); 101 // } 102 // } 103 // } 104 105 // 改进:中间产生的最优不需要,所以直接返过来,从后往前输出即可 106 int row = choice.length - 1; 107 int col = choice[0].length - 1; 108 while (row > 0 && col > 0) { 109 if (choice[row][col] == 1) { 110 System.out.printf("选了第%d件物品\n", row); 111 // 减去当前放入的第row件物品,让其去找前i - 1件物品装入col - w[row - 1]时的最大价值 112 col -= w[row - 1]; 113 } 114 // 直接去找前row - 1件物品装入col时的最大价值 115 row--; 116 } 117 } 118 }
运行结果: