背包问题总结
1. 01背包问题
有 N N N 件物品和一个容量是 V V V的背包。每件物品只能使用一次。
第 i i i 件物品的体积是 v i v_i vi,价值是 w i w_i wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。
输入格式
第一行两个整数 N , V N,V N,V,用空格隔开,分别表示物品数量和背包容积。
接下来有 N N N 行,每行两个整数 v i , w i v_i,w_i vi,wi,用空格隔开,分别表示第 i i i件物品的体积和价值。
输出格式
输出一个整数,表示最大价值。
数据范围
0 < N , V ≤ 10000 < N , V ≤ 1000 0<N,V≤10000<N,V≤1000 0<N,V≤10000<N,V≤1000
0 < v i , w i ≤ 10000 < v i , w i ≤ 1000 0<v_i,w_i≤10000<v_i,w_i≤1000 0<vi,wi≤10000<vi,wi≤1000
输入样例
4 5
1 2
2 4
3 4
4 5
输出样例:
8
分析过程:
-
定义 f ( i , j ) f(i,j) f(i,j)表示第 1 到 i 1到i 1到i个物品(包含第 1 1 1个和第 i i i个物品)满足总体积不超过 j j j的方案价值量
-
那么 f ( i , j ) f(i,j) f(i,j)可以表示为两部分:
- 第一部分:不包含第 i i i个物品的
- 第二部分:包含第 i i i个物品的
对于第一部分,我们可以看成是第 1 到 ( i − 1 ) 1到(i-1) 1到(i−1)个(包含第 1 1 1个和第 i − 1 i-1 i−1个物品),那么就可以表示成为 f ( i − 1 , j ) f(i-1,j) f(i−1,j)
对于第二部分,我们可以看成是前面可以任选,但是后面也一定要携带上第 i i i个物品,因此可以看成是前面 i − 1 i-1 i−1个物品,然后占用的体积是 j − v [ i ] j-v[i] j−v[i],然后加上第 i i i件物品的价值量 w [ i ] w[i] w[i],如下列式子:
f ( i − 1 , j − v [ i ] ) + w [ i ] f(i-1,j-v[i]) + w[i] f(i−1,j−v[i])+w[i]
然后最终的最大值可以表示为
f ( i , j ) = M a t h . m a x ( f ( i − 1 , j ) , f ( i − 1 , j − v [ i ] ) + w [ i ] ) f(i,j) = Math.max(f(i-1,j),f(i-1,j-v[i]) + w[i]) f(i,j)=Math.max(f(i−1,j),f(i−1,j−v[i])+w[i])
因此可以用二维的数组来表示这种状态。
不过一般这种二维dp的我们可以将其优化成一维dp的,仔细观察上式,我们可以发现只需要记录不包含第 i i i个物体的最大价值量和记录包含第 i i i个物体的最大价值量,因此,可以考虑用一个
f ( j ) = M a t h . m a x ( f ( j ) , f ( j − v [ i ] ) + w [ i ] ) f(j) = Math.max(f(j),f(j-v[i]) + w[i]) f(j)=Math.max(f(j),f(j−v[i])+w[i])
一维数组来表示。考虑到数组下标要有意义即大于0,因此我们需要保证 0 ≤ j − v [ i ] 0≤j-v[i] 0≤j−v[i]。
完整的Java代码如下:
import java.io.*;
import java.util.*;
public class Main {
private static int n;
private static int m;
private static int[] v = new int[1010];
private static int[] w = new int[1010];
private static int[] f = new int[1010];
public static void main(String[] args) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out));
String[] num = reader.readLine().split(" ");
int n = Integer.parseInt(num[0]);
int m = Integer.parseInt(num[1]);
for (int i = 1; i <= n; i++) {
String[] arr = reader.readLine().split(" ");
v[i] = Integer.parseInt(arr[0]);
w[i] = Integer.parseInt(arr[1]);
}
for (int i = 1; i <= n; i++) {
for (int j = m; j >= v[i]; j--) {
f[j] = Math.max(f[j],f[j - v[i]] + w[i]);
}
}
writer.write(f[m] + "\n");
writer.flush();
writer.close();
reader.close();
}
}