Skip to content

Commit c46a0b4

Browse files
author
dai
committed
0-1背包OC
1 parent 491a7dc commit c46a0b4

File tree

1 file changed

+116
-0
lines changed

1 file changed

+116
-0
lines changed

28-算法面试专题-动态规划.md

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,122 @@ package com.xiaodai.algorithm;
88
*/
99
public 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

Comments
 (0)