动态规划
背包问题
动态规划专题——背包问题_动态规划 背包_Iareges的博客-CSDN博客
有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。第 i 件物品的体积是 vi,价值是 wi。求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。输出最大价值。
-
输入格式:第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 件物品的体积和价值。
-
输出格式:输出一个整数,表示最大价值。
-
数据范围:0<N,V≤1000,0<vi,wi≤1000
-
输入样例
4 5
1 2
2 4
3 4
4 5
- 输出样例:
8
算法分析
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int N = in.nextInt();
int V = in.nextInt();
int[] v = new int[N+1];
int[] w = new int[N+1];
for (int i = 1; i < N+1; i++) {
v[i] = in.nextInt();
w[i] = in.nextInt();
}
int[][] res = new int[N+1][V+1];
for (int i = 1; i < N+1; i++) { //内外两层循环可以互换位置
for (int j = 1; j < V+1; j++) {
if(j < v[i]) res[i][j] = res[i-1][j]; //装不下第i个物品
else res[i][j] = Math.max(res[i-1][j],res[i-1][j-v[i]]+w[i]);
}
}
System.out.println(res[N][V]);
}
}
每种物品只有放与不放的选择,i 遍历1~N表示只考虑前 i 个物品,每次遍历体积1~V的情况。针对 res[i][j]
,第 i 个物品放不下就之间取 res[i][j]
,放得下就判断 不放的 res[i-1][j]
还是放了的 res[i-1][j-v[i]]+w[i]
(腾出来第 i 个物品体积 v[i]
的空间) 值大。每次得到的都是在考虑前 i 个物品、体积不超过 j 的情况下最大的价值
f(i, j) 表示只考虑前 i 个物品、且总体积不超过 j 的范围下的 max 。有两种情况
- 不选第 i 个物品 → f(i-1, j)
- 选第 i 个物品 → 前 i-1 的部分是找最大值+第 i 个物品的价值 → f(i-1, j-vi) + w[i]。注意要是 j 比第 i 个的体积还小就放不下,这种情况不成立
- 取上面两种情况的最大值
优化后
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int N = in.nextInt();
int V = in.nextInt();
int[] v = new int[N+1];
int[] w = new int[N+1];
for (int i = 1; i < N+1; i++) {
v[i] = in.nextInt();
w[i] = in.nextInt();
}
int[] f = new int[V+1];
for (int i = 1; i < N+1; i++) {
for (int j = V; j >= v[i]; j--)
f[j] = Math.max(f[j],f[j-v[i]]+w[i]);
}
System.out.println(f[V]);
}
}
完全背包问题
在上面基础上,每次选第 i 个物品时,可以选0、1、2、…、[
j
w
[
i
]
\frac{j}{w[i]}
w[i]j]个。在上面的基础上再加一个 1~[
j
w
[
i
]
\frac{j}{w[i]}
w[i]j]循环即可
f
[
i
]
[
j
]
=
max
0
≤
k
≤
t
f
[
i
−
1
]
[
j
−
k
⋅
w
[
i
]
]
+
k
⋅
v
[
i
]
f[i][j]=\max_{0≤k≤t} f[i−1][j−k⋅w[i]]+k⋅v[i]
f[i][j]=0≤k≤tmaxf[i−1][j−k⋅w[i]]+k⋅v[i]
for (int i = 1; i <= N; i++)
for (int j = 1; j <= V; j++) {
int t = j / w[i];
for (int k = 0; k <= t; k++)
f[i][j] = max(f[i][j], f[i - 1][j - k * w[i]] + k * v[i]);
}
优化后
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int N = in.nextInt();
int V = in.nextInt();
int[] v = new int[N+1];
int[] w = new int[N+1];
for (int i = 1; i < N+1; i++) {
v[i] = in.nextInt();
w[i] = in.nextInt();
}
int[] f = new int[V+1];
for (int i = 1; i < N+1; i++) {
for (int j = v[i]; j < V+1; j++)
f[j] = Math.max(f[j],f[j-v[i]]+w[i]);
}
System.out.println(f[V]);
}
}
其实将01背包问题的优化后算法的内层循环变成由小到大即可
多重背包问题
在上面两种的基础上,每次取的第 i 个物品可取的个数限制在 s[i] 和容量之间,能取到的个数为 t = m i n ( s [ i ] , j w [ i ] ) t=min(s[i],\frac{j}{w[i]}) t=min(s[i],w[i]j) ,对 k 取值再做一个小于的范围即可
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int N = in.nextInt();
int V = in.nextInt();
int[] v = new int[N+1];
int[] w = new int[N+1];
int[] s = new int[N+1];
for (int i = 1; i < N+1; i++) {
v[i] = in.nextInt();
w[i] = in.nextInt();
s[i] = in.nextInt();
}
int[] f = new int[V+1];
for (int i = 1; i <= N; i++)
for (int j = V; j >= 0; j--){
for (int k = 1; k <= s[i] && k*v[i] <= j; k++)
f[j] = Math.max(f[j], f[j-k*v[i]] + k*w[i]);
}
System.out.println(f[V]);
}
}