@@ -8,6 +8,122 @@ package com.xiaodai.algorithm;
8
8
*/
9
9
public class DPExampleUtil {
10
10
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
+ }
11
127
12
128
13
129
}
0 commit comments