有 N种物品和一个容量是 V 的背包,每种物品都有无限件可用。
第 i 种物品的体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。
输入格式 :
输出一个整数,表示最大价值。
数据范围:
0<N,V≤1000
0<vi,wi≤1000
输入样例 :
4 5
1 2
2 4
3 4
4 5
输出样例:
10
解题思路:对于一个状态,考虑前i件物品,背包容量为j的情况下,所能装入的最大价值我们设为 f(i,j)。另外vi,wi分别代表着第i个物品的体积与价值。
刚好与我们设的数组f[][]对应。那么对于第i件物品我们有装与不装两种情况。
1.不装,那么最大价值就等于:考虑前i-1件物品,背包容量为j的情况下,所能装入的最大价值,对应f(i-1,j)。
2.装,由于物品无限多,所以又分为装1,2,....k个这些装法所对应的最大值分别为f(i-1,j-vi)+wi,f(i-1,j-2vi)+2wi,....f(i-1,j-kvi)+kwi。
综上:考虑前i件物品,背包容量为j的情况下,所能装入的最大价值:
一式:f(i , j) = max(f( i - 1, j),f(i - 1, j - vi) + wi ,f(i - 1,j - 2vi) + 2wi,....f(i - 1, j - kvi) + kwi)
这是一个状态方程:我们令j = j - vi ,代入上式得
二式:f(i , j - vi) = max(f(i - 1, j - vi) , f(i - 1,j - 2vi) + wi,....f(i - 1, j - kvi) + (k-1)wi,f(i - 1, j - (k+1)vi) + kwi)。
第i件物品最多只能装k件,所以二式中最后细字体一项不成立,舍去。
仔细观察二式中红色字体每一项都比下一项多一个wi,所以一式红色部分等效于f(i, j - vi) + wi。
所以一式优化后的方程为:f(i , j) = max(f( i - 1, j), f(i, j - vi) + wi)。
这就是完全背包问题的最优状态方程。
理论成立代码如下:
朴素版:
import java.util.*;
public class Main {
public static int N = 1010;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int m = sc.nextInt();
int v[] = new int[N];
int w[] = new int[N];
int f[][] = new int [N][N];//方程f(i,j)
for(int i = 1; i <=n; i++) {
v[i] = sc.nextInt();
w[i] = sc.nextInt();
}
for(int i = 1; i<=n; i++)
for(int j = 0; j<=m; j++) {
f[i][j] = f[i-1][j];//不装
if(j >= v[i])//符合条件才会考虑装入
f[i][j] = Math.max(f[i][j], f[i][ j - v[i]]+w[i]);
}
System.out.print(f[n][m]);
}
}
值得一提的是01背包的状态方程为:f(i,j) = max(f(i - 1, j),f(i - 1, j - vi)+wi)。两者很相似但是有略微区别。
精华版:
import java.util.*;
public class Main {
public static int N = 1010;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int m = sc.nextInt();
int v[] = new int[N];
int w[] = new int[N];
int f[] = new int [N];//方程f(i,j)
for(int i = 1; i <=n; i++) {
v[i] = sc.nextInt();
w[i] = sc.nextInt();
}
for(int i = 1; i<=n; i++)
for(int j = v[i]; j<=m; j++) f[j] = Math.max(f[j], f[ j - v[i]]+w[i]);
System.out.print(f[m]);
}
}