@@ -8,6 +8,122 @@ package com.xiaodai.algorithm;
88 */
99public class DPExampleUtil {
1010
11+ /**
12+ * 1、🎒背包问题:给定两个长度都为N的数组weights和values,weight[i]和values[i]分别代表i号物品的重量和价值。
13+ * 给定一个正数bag,表示一个载重bag的袋子,你装的物品不能超过这个重量。返回你能装下最多的价值是多少?
14+ *
15+ * @param w 重量数组
16+ * @param v 价值数组
17+ * @param bag 背包的最大容量
18+ * @return 返回该背包所能装下的最大价值
19+ */
20+ public static int getMaxValue (int [] w , int [] v , int bag ) {
21+ // 初始传入w,v。index位置开始,alreadyW表示在index位置的时候,重量已经到达了多少
22+ return process(w, v, 0 , 0 , bag);
23+ }
24+
25+ // 暴力递归的第一种尝试
26+ // 0..index-1上做了货物的选择,使得你已经达到的重量是多少 alreadyW
27+ // 如果返回-1,认为没有方案
28+ // 如果不返回-1,认为返回的值是真实价值
29+ public static int process (int [] w , int [] v , int index , int alreadyW , int bag ) {
30+ // base case
31+ if (alreadyW > bag) {
32+ return - 1 ;
33+ }
34+ // 重量没超
35+ if (index == w. length) {
36+ return 0 ;
37+ }
38+ // 当前不选择index的货物情况下,后续的价值
39+ // 无需传递当前index的重量,且p1就是总价值
40+ int p1 = process(w, v, index + 1 , alreadyW, bag);
41+ // 当前选择了index的货物,把重量加上,继续向下递归
42+ int p2next = process(w, v, index + 1 , alreadyW + w[index], bag);
43+ // p2表示要了当前货物之后总价值应该是后续价值加上当前价值
44+ int p2 = - 1 ;
45+ if (p2next != - 1 ) {
46+ p2 = v[index] + p2next;
47+ }
48+ return Math . max(p1, p2);
49+
50+ }
51+
52+
53+ /**
54+ * 背包问题的第二种暴力尝试。
55+ *
56+ * @param w 重量数组
57+ * @param v 价值数组
58+ * @param bag 背包容量
59+ * @return 返回给定背包容量所能装下的最大价值
60+ */
61+ public static int maxValue (int [] w , int [] v , int bag ) {
62+ // 相比上一个暴力递归尝试,去掉了alreadyW。用背包剩余空间代替;rest表示背包剩余空间,初始剩余空间就是背包容量
63+ return process(w, v, 0 , bag);
64+ }
65+
66+ public static int process (int [] w , int [] v , int index , int rest ) {
67+ // base case 1 无效方案。背包剩余容量装不下当前重量的情况
68+ if (rest < 0 ) {
69+ return - 1 ;
70+ }
71+ // rest >=0。index来到终止位置,没货物了,当前返回0价值
72+ // base case 2
73+ if (index == w. length) {
74+ return 0 ;
75+ }
76+ // 有货也有空间。当前index不选择,得到p1总价值
77+ int p1 = process(w, v, index + 1 , rest);
78+ int p2 = - 1 ;
79+ // 选择了index位置,剩余空间减去当前重量
80+ int p2Next = process(w, v, index + 1 , rest - w[index]);
81+ // 选择index的总价值,是index...的价值加上个当前index的价值
82+ if (p2Next != - 1 ) {
83+ p2 = v[index] + p2Next;
84+ }
85+ return Math . max(p1, p2);
86+ }
87+
88+
89+ /**
90+ * 0-1背包问题:动态规划解决方案。在暴力递归的思路上改进
91+ * <p >
92+ * 以背包问题举例,我们每一个重量有要和不要两个选择,且都要递归展开。那么我们的递归时间复杂度尾O(2^N)。
93+ * 而记忆化搜索,根据可变参数得到的长为N价值为W的二维表,那么我们的时间复杂度为O(N*bag)。
94+ * 如果递归过程中状态转移有化简继续优化的可能,例如枚举。那么经典动态规划可以继续优化,
95+ * 否则记忆化搜索和动态规划的时间复杂度是一样的
96+ *
97+ * @param w 重量数组
98+ * @param v 价值数组
99+ * @param bag 背包容量
100+ * @return 返回价值
101+ */
102+ public static int dpWay (int [] w , int [] v , int bag ) {
103+ int N = w. length;
104+ // 准备一张dp表,行号为我们的重量范围bag+1。列为我们的价值数目个数的范围N+1。dp数组装下所有的可能性。
105+ int [][] dp = new int [N + 1 ][bag + 1 ];
106+ // 由于暴力递归中index==w.length的时候,总是返回0。所以:
107+ // dp[N][...] = 0。整形数组初始化为0,无需处理
108+ // 由于N行已经初始化为0,我们从N-1开始。填我们的dp表
109+ for (int index = N - 1 ; index >= 0 ; index-- ) {
110+ // 剩余空间从0开始,一直填写到bag
111+ for (int rest = 0 ; rest <= bag; rest++ ) { // rest < 0
112+ // 通过正常位置的递归处理。我们转而填写我们的dp表
113+ // 所以我们p1等于dp表的下一层向上一层返回
114+ int p1 = dp[index + 1 ][rest];
115+ int p2 = - 1 ;
116+ // rest - w[index] 不越界
117+ if (rest - w[index] >= 0 ) {
118+ p2 = v[index] + dp[index + 1 ][rest - w[index]];
119+ }
120+ // p1和p2取最大值
121+ dp[index][rest] = Math . max(p1, p2);
122+ }
123+ }
124+ // 最终返回dp表的0,bag位置,就是我们暴力递归的主函数调用
125+ return dp[0 ][bag];
126+ }
11127
12128
13129}
0 commit comments