-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathBagProblem.java
More file actions
380 lines (369 loc) · 15.4 KB
/
BagProblem.java
File metadata and controls
380 lines (369 loc) · 15.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
import java.util.*;
class Solution{
public static void main(String[] args) {
int[] v = new int[]{0,1,2,3,4};
int[] w = new int[]{0,2,4,4,5};
int N = 4;
int V = 5;
System.out.println("**************** 1");
System.out.println(maxValue1(V,N,v,w));
System.out.println("**************** 2");
System.out.println(maxValue2(V,N,v,w));
System.out.println("**************** 3");
System.out.println(maxValue3(V,N,v,w));
System.out.println("**************** 4");
System.out.println(maxValue4(V,N,v,w));
System.out.println("**************** 5");
System.out.println(maxValue5(V,N,v,w));
System.out.println("**************** 6");
int[] s = new int[]{0,3,1,3,2};
System.out.println(maxValue6(V,N,v,w,s));
System.out.println("**************** 7");
System.out.println(maxValue7(V,N,v,w,s));
System.out.println("**************** 8");
System.out.println(maxValue8(V,N,v,w,s));
System.out.println("**************** 9");
int[] s2 = new int[]{0,-1,1,0,2};
System.out.println(maxValue9(V,N,v,w,s2));
System.out.println("**************** 10");
System.out.println(maxValue10(V,N,v,w,s2));
System.out.println("**************** 11");
int M = 6;
int[] m = new int[]{0,2,4,4,5};
int[] w2 = new int[]{0,3,4,5,6};
System.out.println(maxValue11(V,N,M,v,w2,m));
System.out.println("**************** 12");
maxValue12();
System.out.println("**************** 13");
System.out.println(maxValue13(V,N,v,w));
System.out.println("**************** 14");
System.out.println(maxValue14(V,N,v,w));
System.out.println("**************** 15");
}
public static void print(int[][] arrs){
for(int[] arr:arrs){
for(int i:arr) System.out.print(i+" ");
System.out.println();
}
}
/**
背包问题:1 : 0-1背包问题
问题描述:有一个容量为V的背包,然后有N个物品,N个物品的价值与体积分别为w_i,v_i,且每一个物品最多只能使用一次,求用该背包能装取的最大价值是多少?
状态定义:dp[i][j]表示前i个物品放入容量是j的背包里所能获得的最大的价值
状态转移方程:
dp[i][j] = dp[i-1][j] , if not choose item i , 表示前i-1个物品占用j空间的最大价值
dp[i][j] = Math.max(dp[i-1][j],dp[i-1][j-v[i]] + w[i]) ,if choose item i,表示选择第i个物品,那么就相当于是从体积j-v[i]转移过来的状态
初始状态:dp[0][0] = 0
时间复杂度一般而言是不能够进行优化的,但是可以对空间复杂度进行优化,
状态定义:dp[j] 表示体积为j的时候,所能装的物品的最大价值
状态转移方程:dp[j] = max(dp[j],dp[j-v[i]] + w[i]) 但是要注意的是第二层循环中,要从大到小进行遍历,不然的话,因为若是继续从小到大循环,后面算的时候,用的是这一层已经算过的数据,就变成dp[i][j]=max(dp[i][j],dp[i][j-v[i]]+w[i]) ,(这正好是完全背包一维的解法,每个物品可以选无限次)而从大到小算的话一定用的是上一层的状态
初始状态:dp[...] = 0
*/
public static int maxValue1(int V,int N,int[] v,int[] w){
int[][] dp = new int[N+1][V+1];
for(int i = 1;i<=N;i++){
for(int j = 1;j<=V;j++){
if(j>=v[i]) dp[i][j] = Math.max(dp[i-1][j],dp[i-1][j-v[i]]+w[i]);
else dp[i][j] = dp[i-1][j];
}
}
print(dp);
return dp[N][V];
}
public static int maxValue2(int V,int N,int[] v,int[] w){
int[] dp = new int[V+1];
for(int i = 1;i<N;i++){
for(int j = V;j>=v[i];j--){
dp[j] = Math.max(dp[j],dp[j-v[i]]+w[i]);
}
}
for(int i:dp) System.out.print(i+" ");
System.out.println();
return dp[V];
}
/**
背包问题:2 : 完全背包问题
问题描述:有一个容量为V的背包,然后有N个物品,N个物品的价值与体积分别为w_i,v_i,且每一个物品可以用多次,求用该背包能装取的最大价值是多少?
状态定义:dp[i][j]表示前i个物品放入容量是j的背包里所能获得的最大的价值
状态转移方程:
dp[i][j] = dp[i-1][j] , if not choose item i , 表示前i-1个物品占用j空间的最大价值
dp[i][j] = Math.max(dp[i-1][j],dp[i-1][j-v[i]] + w[i]) ,if choose item i,表示选择第i个物品,那么就相当于是从体积j-v[i]转移过来的状态
初始状态:dp[0][0] = 0
时间复杂度一般而言是不能够进行优化的,但是可以对空间复杂度进行优化,
状态定义:dp[j] 表示体积为j的时候,所能装的物品的最大价值
状态转移方程:dp[j] = Math.max(dp[j],dp[j-v[i]]+w[i])
初始状态:dp[0][0] = 0
*/
public static int maxValue3(int V,int N,int[] v,int[] w){
int[][] dp = new int[N+1][V+1];
for(int i=1;i<=N;i++){
for(int j = 1;j<=V;j++){
for(int k=0;k*v[i]<=j;k++) {
dp[i][j]=Math.max(dp[i][j], dp[i-1][j-k*v[i]]+w[i]*k);
}
}
}
print(dp);
return dp[N][V];
}
public static int maxValue4(int V,int N,int[] v,int[] w){
int[][] dp = new int[N+1][V+1];
for(int i=1;i<=N;i++){
for(int j = 1;j<=V;j++){
if(j>=v[i]) dp[i][j] = Math.max(dp[i-1][j],dp[i][j-v[i]]+w[i]);
else dp[i][j] = dp[i-1][j];
}
}
print(dp);
return dp[N][V];
}
public static int maxValue5(int V,int N,int[] v,int[] w){
int[] dp = new int[V+1];
for(int i = 1;i<N;i++){
for(int j = v[i];j<=V;j++){
dp[j] = Math.max(dp[j],dp[j-v[i]]+w[i]);
}
}
for(int i:dp) System.out.print(i+" ");
System.out.println();
return dp[V];
}
/**
背包问题:3 : 多重背包问题
问题描述:有 N种物品和一个容量是V的背包,第 i种物品最多有 s_i件,每件体积是 v_i,价值是 w_i,求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。输出最大价值。
状态定义:dp[i][j]表示前i个物品放入容量是j的背包里所能获得的最大的价值
状态转移方程:
dp[i][j]=Math.max(dp[i][j], dp[i-1][j-k*v[i]]+w[i]*k)
初始状态:dp[0][0] = 0
空间优化:
状态定义:dp[j] 表示体积为j的时候,所能装的物品的最大价值
状态转移方程:dp[j]=Math.max(dp[j], dp[j-k*v[i]]+w[i]*k);
初始状态:dp[...] = 0
二进制优化,时间上和空间上的优化
二进制优化思想:这道题的数据范围如果用三层循环的话是达到了1e9,所以必须优化它。其实可以把它转化为一个01背包的问题。每个物品有s件,我们可以把它差分成s份,每份物品当做不同的个体,即只能选一次,这就转化为了01背包物品,但是这样的话,物品个数变成了1000*2000=2e6,再循环一层空间的话,还是1e9的复杂度。
那么继续优化,一个物品的数量是s的话,只要把s拆分成一些数字,使它们能够表示出1-s中任意一个数字,就可以,没必要把它拆成s个1。那么这样的数字最少需要多少个呢?最少需要log(s)个,向上取整。
比如7,它最少需要3个数字来表示:
即 1(2^0=1 ), 2(2^1=2), 4(2^2=4)。
原因:每个数字有2种可能选或不选,那么可以表示的不同数字个数就是 2 * 2 * 2 = 8。但是还需要注意一个问题,就是有些数字可能能够表示出来一些大于s的数字,但是这件物品最多只有s件,那么就需要特殊处理一下最后一个数。
状态定义:dp[j] 表示体积为j的时候,所能装的物品的最大价值
状态转移方程:dp[j] = Math.max(dp[j],dp[j-v2[i]]+w2[i]);
初始状态:dp[...] = 0
*/
public static int maxValue6(int V,int N,int[] v,int[] w,int[] s){
int[][] dp = new int[N+1][V+1];
for(int i=1;i<=N;i++){
for(int j = 1;j<=V;j++){
for(int k=0;k<=s[i] && k*v[i]<=j;k++) {
dp[i][j]=Math.max(dp[i][j], dp[i-1][j-k*v[i]]+w[i]*k);
}
}
}
print(dp);
return dp[N][V];
}
public static int maxValue7(int V,int N,int[] v,int[] w,int[] s){
int[] dp = new int[V+1];
for(int i=1;i<=N;i++){
for(int j = V;j>=v[i];j--){
for(int k=0;k<=s[i] && k*v[i]<=j;k++) {
dp[j]=Math.max(dp[j], dp[j-k*v[i]]+w[i]*k);
}
}
}
for(int i:dp) System.out.print(i+" ");
System.out.println();
return dp[V];
}
public static int maxValue8(int V,int N,int[] v,int[] w,int[] s){
int[] dp = new int[V+1];
//01背包相当于物品件数为1的多重背包
int[] v2 = new int[N*V];
int[] w2 = new int[N*V];
// 遍历所有的物品的数量
int k = 1;
for(int i = 1;i<s.length;i++){
// 记录2的多少次方
int j = 1;
while(j<=s[i]){
v2[k] = v[i]*j;
w2[k++] = w[i]*j;
s[i]-=j;
j*=2;
}
if(s[i] >0){
v2[k] = v[i] * s[i];
w2[k++] = w[i] * s[i];
}
}
for(int i = 1;i<k;i++){
for(int j = V;j>=v2[i];j--){
dp[j] = Math.max(dp[j],dp[j-v2[i]]+w2[i]);
}
}
return dp[V];
}
/**
背包问题:4 : 混合背包问题
问题描述:有N种物品和一个容量是V的背包。物品一共有三类:第一类物品只能用1次(01背包);第二类物品可以用无限次(完全背包);第三类物品最多只能用 s_i 次(多重背包);每种体积是 v_i,价值是 w_i。求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。输出最大价值。
si=−1 表示第 i 种物品只能用1次;
si=0 表示第 i 种物品可以用无限次;
si>0 表示第 i 种物品可以使用 si 次
思路:是一个前三种背包问题的综合,如果明白了前面的,就很简单了,只需要判断一下类型,如果是多重背包,将它转换为01背包插入数组当中,然后按着不同类型的处理方式去遍历空间大小即可
*/
public static int maxValue9(int V,int N,int[] v,int[] w,int[] s){
int[] dp = new int[V+1];
//01背包相当于物品件数为1的多重背包
for(int i =0;i<s.length;i++) if(s[i] == -1) s[i] = 1;
for(int i = 1;i<=N;i++){
// 完全背包问题
if(s[i] == 0){
for(int j = v[i];j<=V;j++) dp[j] = Math.max(dp[j],dp[j-v[i]]+w[i]);
}else{
// 多重背包问题
for(int j = V;j>=v[i];j--){
for(int k=0;k<=s[i] && k*v[i]<=j;k++) {
dp[j]=Math.max(dp[j], dp[j-k*v[i]]+w[i]*k);
}
}
}
}
return dp[V];
}
/**
利用二进制优化
*/
public static int maxValue10(int V,int N,int[] v,int[] w,int[] s){
int[] dp = new int[V+1];
//01背包相当于物品件数为1的多重背包
for(int i = 0; i < N; i++){
if(s[i] == 0){
// 完全背包问题
for(int j = v[i]; j <= V; j++){
dp[j] = Math.max(dp[j], dp[j - v[i]] + w[i]);
}
}else{
// 多重背包问题,01背包是多重背包的特例,可以一并处理
s[i] = Math.abs(s[i]);
for(int j = 1; s[i] >= j; s[i] -= j, j *= 2){
for(int k = V; k >= j * v[i]; k--){
dp[k] = Math.max(dp[k], dp[k - j * v[i]] + j * w[i]);
}
}
if(s[i] > 0){
for(int j = V; j >= s[i] * v[i]; j--){
dp[j] = Math.max(dp[j], dp[j - s[i] * v[i]] + s[i] * w[i]);
}
}
}
}
return dp[V];
}
/**
背包问题:5 : 二维费用的背包问题
问题描述:有 N 件物品和一个容量是 V 的背包,背包能承受的最大重量是 M。每件物品只能用一次。体积是 vi,重量是 mi,价值是 wi。
求解将哪些物品装入背包,可使物品总体积不超过背包容量,总重量不超过背包可承受的最大重量,且价值总和最大。输出最大价值
*/
public static int maxValue11(int V,int N,int M,int[] v,int[] w,int[] m){
int[][] dp = new int[V+1][M+1];
for(int i=1;i<=N;i++){
for(int j =V;j>=v[i];j--){
for(int k = M;k>=m[i];k--){
dp[j][k] = Math.max(dp[j][k],dp[j-v[i]][k-m[i]]+w[i]);
}
}
}
return dp[V][M];
}
/**
背包问题:6 : 分组背包问题
问题描述:有 N 组物品和一个容量是 V 的背包。每组物品有若干个,同一组内的物品最多只能选一个。
每件物品的体积是 vij,价值是 wij,其中 i 是组号,j 是组内编号。求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。输出最大价值。
*/
public static int maxValue12(){
int v = 10;
int n = 3;
int[] dp = new int[v+1];
int[][] w = new int[][]{{2, 3, 5}, {1, 3, 2}, {7, 4, 2, 6}};
int[][] p = new int[][]{{4, 6, 7}, {1, 4, 8}, {3, 6, 2, 4}};
// 遍历背包容量大小
for(int j = v; j >= 0; j--){
// 当前组有w.length个物品
for(int i = 0; i < w.length; i++){
// 遍历每一个背包中的物品重量
for(int k = 0;k<w[i].length;k++){
if(j >= w[i][k]){
dp[j] = Math.max(dp[j],dp[j-w[i][k]] + p[i][k]);
}
}
}
}
System.out.println(dp[v]);
return 0;
}
/**
背包问题:7 : 有依赖的背包问题(树形DP+背包问题)
问题描述:
*/
/**
背包问题:8 : 背包问题求方案数
问题描述:有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。第 i 件物品的体积是 vi,价值是 wi。求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。输出 最优选法的方案数。注意答案可能很大,请输出答案模 10^9+7 的结果。
思路:在原来01背包的基础上加一个表示方案数的数组即可。注意初始化,如果只把num[0]赋值成1,那么需要把对应的01背包转化为体积恰好是j的情况下的最大价值。最后找出最大价值,将此价值对应的所有体积的方案数加上即可。
*/
public static int maxValue13(int V,int N,int[] v,int[] w){
// dp1[j] 表示当前容量为j时的最大价值
int[] dp1 = new int[V+1];
// dp2[j]表示当前容量为j时的最大价值的方案数目
int[] dp2 = new int[V+1];
Arrays.fill(dp1,Integer.MIN_VALUE);
// 就算一个也不拿,也是一种方案
Arrays.fill(dp2,1);
for(int i=1;i<=N;i++){
for(int j = V;j>=v[i];j--){
int temp = dp1[j-v[i]] + w[i];
if(temp == dp1[j]) {
dp2[j]+=dp2[j-v[i]];
}
else if(dp1[j] < temp){
dp1[j] = temp;
dp2[j] = dp2[j-v[i]];
}
dp2[j]%=1000000007;
}
}
int maxw = Integer.MIN_VALUE;
for(int i=1;i<=V;i++)
maxw=Math.max(maxw,dp1[i]);
int ans=0;
for(int i=1;i<=V;i++)
if(dp1[i]==maxw)
ans=(ans+dp2[i])%1000000007;
return ans;
}
/**
背包问题:9 : 背包问题求具体方案
问题描述:有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。第 i 件物品的体积是 vi,价值是 wi。求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。输出 字典序最小的方案。这里的字典序是指:所选物品的编号所构成的序列。物品的编号范围是 1…N。
*/
public static int maxValue14(int V,int N,int[] v,int[] w){
// 就是记录转移方案
int[][] dp = new int[N+2][V+2];
for(int i = N;i>=1;i--){
for(int j = 0;j<=V;j++){
dp[i][j] = dp[i+1][j];
if(j>=v[i]) dp[i][j] = Math.max(dp[i][j],dp[i+1][j-v[i]]+w[i]);
}
}
int x = V;
for(int i = 1;i<=N;i++){
if(x-v[i]>=0 && dp[i][x] == dp[i+1][x-v[i]]+w[i]){
System.out.print(i + " ");
x-=v[i];
}
}
System.out.println();
for(int[] info:dp){
for(int i:info) System.out.print(i+" ");
System.out.println();
}
return 0;
}
}