DP
01背包
有 $N$ 件物品和一个容量是 $V$ 的背包。每件物品只能使用一次。
第 $i$ 件物品的体积是 $vi$,价值是 $w_i$。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。输入格式
第一行两个整数,$N,V$,用空格隔开,分别表示物品数量和背包容积。
接下来有 $N$ 行,每行两个整数 $v_i,w_i$,用空格隔开,分别表示第 $i$ 件物品的体积和价值。
输出格式
输出一个整数,表示最大价值。
数据范围
$0<N,V≤1000$
$0<v_i,w_i≤1000$输入样例
4 5 1 2 2 4 3 4 4 5
输出样例:
8
public class Beg_01 {
final static int N = 10010;
final static int[] w = new int[N], v = new int[N];
// 未优化版
final static int[][] f = new int[N][N];
// 优化版
final static int[] g = new int[N];
public static void main(String[] args) {
Scanner cin = new Scanner(System.in);
int n = cin.nextInt(), m = cin.nextInt();
for (int i = 1; i <= n; i++) {
v[i] = cin.nextInt(); w[i] = cin.nextInt();
}
// 朴素版
prime(n, m);
// 优化版
optimization(n, m);
}
private static void prime(int n, int m) {
// i 指向第 i 个物品
for (int i = 1; i <= n; i++) {
// j 指向容量
for (int j = 1; j <= m; j++) {
// 没有将第 i 个物品加入背包的情况
f[i][j] = f[i - 1][j];
// 将第 i 个物品加入背包的情况
if (v[i] <= j) f[i][j] = Math.max(f[i][j], f[i - 1][j - v[i]] + w[i]);
}
}
System.out.println(f[n][m]);
}
private static void optimization(int n, int m) {
// i 指向第 i 个物品
for (int i = 1; i <= n; i++) {
// j 指向容量
for (int j = m; j >= v[i]; j--) {
// 将第 i 个物品加入背包的情况
g[j] = Math.max(g[j], g[j - v[i]] + w[i]);
}
}
System.out.println(g[m]);
}
}
完全背包
有 $N$ 种物品和一个容量是 $V$ 的背包,每种物品都有无限件可用。
第 $i$ 种物品的体积是 $v_i$,价值是 $w_i$。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。输入格式
第一行两个整数,$N,V$,用空格隔开,分别表示物品种数和背包容积。
接下来有 $N$ 行,每行两个整数 $v_i,w_i$,用空格隔开,分别表示第 $i$ 种物品的体积和价值。
输出格式
输出一个整数,表示最大价值。
数据范围
$0<N,V≤1000$
$0<v_i,w_i≤1000$输入样例
4 5 1 2 2 4 3 4 4 5
输出样例:
10
/**
* Description: 完全背包问题
* 求在前 i 个物品中,在不超过 j 容量的前提下的组合最大值
*/
public class Beg_Complete {
final static int N = 10010;
final static int[] v = new int[N], w = new int[N];
final static int[][] f = new int[N][N];
final static int[] g = new int[N];
public static void main(String[] args) {
int n = Tools.getInt(), m = Tools.getInt();
for (int i = 1; i <= n; i++) {
v[i] = Tools.getInt(); w[i] = Tools.getInt();
}
prime(n, m);
optimization(n, m);
ultimateOptimization(n, m);
}
private static void prime(int n, int m) {
for (int i = 1; i <= n; i++)
for (int j = 0; j <= m; j++)
for (int k = 0; k * v[i] <= j; k++)
f[i][j] = Math.max(f[i][j], f[i - 1][j - v[i] * k] + k * w[i]);
System.out.println(f[n][m]);
}
private static void optimization(int n, int m) {
for (int i = 1; i <= n; i++)
for (int j = 0; j <= m; j++) {
f[i][j] = f[i - 1][j];
if (v[i] <= j) f[i][j] = Math.max(f[i][j], f[i][j - v[i]] + w[i]);
}
System.out.println(f[n][m]);
}
private static void ultimateOptimization(int n, int m) {
for (int i = 1; i <= n; i++)
for (int j = v[i]; j <= m; j++) {
g[j] = Math.max(g[j], g[j - v[i]] + w[i]);
}
System.out.println(g[m]);
}
}
优化过程
令 $j$ 等于 $x + k * v[i]$ 则有 $$f[i][j] = f[i][x + k * v[i]]$$
从题意出发,有 ① $$f[i][x + k * v[i]] = max(f[i - 1][x + k * v[i]], f[i - 1][x + (k - 1) * v[i]] + w[i], ···, f[i - 1][x] + k * w[i])$$
同理可得 ② $$f[i][x + (k - 1) * v[i]] = max(f[i - 1][x + (k - 1) * v[i]], f[i - 1][x + (k - 2) * v[i]] + w[i], ···, f[i - 1][x] + k * w[i])$$
那么整理可得 ③ $$f[i][x + k * v[i]] = max(f[i - 1][x + k * v[i]], f[i][x + (k - 1) * v[i]] + w[i])$$
故 $$f[i][j] = max(f[i - 1][j], f[i][j - v[i]] + w[i])$$
优化为一维,将 $i$ 去除,可得 ④ $$f[j] = max(f[j], f[j - v[i]] + w[i])$$
对比可知 $③ == ④$
对比
类似 01背包问题,但 01背包问题要求只选择一次,故只能由后向前遍历,将下一层的数据覆盖上一层的数据;
完全背包问题则是要求选择多次,可能出现使用同一层的数据进行更新,故只需从前往后遍历,使之覆盖数据就行
参考