完全背包问题
-
问题背景
- 一个容量是V的背包和N种物品,每种物品的体积是v
i,价值是wi,每种物品能无限使用,求能装走物品的最大价值
- 一个容量是V的背包和N种物品,每种物品的体积是v
-
Dp分析
-
状态表示
- 二维状态表示
f(i,j) f(i,j)表示的是哪一个集合:所有满足如下条件的选法集合- 只从前
i个物品中选 - 选出来的物品的总体积小于等于
j
- 只从前
f(i,j)存的是什么属性:Max,Min,数量;在这里f(i,j)存的应该是最大值,即所有满足这种选法的最大值
- 二维状态表示
-
状态计算:
f(i,j)可以怎么算出来?- 完全背包问题将集合划分为
k+1个子集- 每次选择
k个第i个物品,k从 0 取到 k * vi<= j - f(i, j) = f(i - 1, j - v
i* k) + wi* k
- 每次选择
- 因此 f(i, j) = max( f(i, j), f(i - 1, j - v
i* k) + wi* k )
- 完全背包问题将集合划分为
-
代码
-
//初始化f[0][0 ~ m]都是0,所以省略 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] + W[i] * k); } } }
-
-
优化
-
观察状态计算的过程
- f(i, j) = max( f(i - 1, j), f(i - 1, j - v
i) + wi, f(i - 1, j - vi* 2) + wi* 2, f(i - 1, j - vi* 3) + wi* 3, …… ) - f(i, j - v
i) = max( f(i - 1, j - vi), f(i - 1, j - vi* 2) + wi, f(i - 1, j - vi* 3) + wi* 2, ……) - 由上面两式,可以得出 f(i, j) = max (f(i - 1, j), f(i, j - v
i) + wi) ,与k无关
- f(i, j) = max( f(i - 1, j), f(i - 1, j - v
-
因此可以做如下优化
- 将
k层循环删掉,变成类似01背包问题的格式
- 将
-
代码
-
//初始化f[0][0 ~ m]都是0,所以省略 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]); } } }
-
-
-
再优化
-
观察状态计算的过程,可以发现两个特点
- f(i, j) 首先先会继承 f(i - 1, j) 的值
- 然后再和当前第
i层在它之前就更新完了的 f(i, j - vi) + wi比较
-
因此可以做如下优化
- 将 i 这一维直接优化掉,i 的每一层循环中的 j 都在原先的 j 上进行覆盖
- j 层循环直接从 v
i开始
-
代码
-
//初始化f[0][0 ~ m]都是0,所以省略 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]); } }
-
-
练习
- 题目
- 题解1
import java.io.*;
import java.util.*;
public class Main {
public static final int N = 1010;
public static int[] V = new int[N];
public static int[] W = new int[N];
//只从前i个物品中选,选出来的物品的总体积小于等于j,满足这两个条件的价值的最大值
public static int[][] f = new int[N][N];
public static int n, m;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));
String[] str1 = br.readLine().split(" ");
n = Integer.parseInt(str1[0]);
m = Integer.parseInt(str1[1]);
for (int i = 1; i <= n; i++) {
String[] str2 = br.readLine().split(" ");
V[i] = Integer.parseInt(str2[0]);
W[i] = Integer.parseInt(str2[1]);
}
//初始化f[0][0 ~ m]都是0,所以省略
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] + W[i] * k);
}
}
}
pw.println(f[n][m]);
br.close();
pw.close();
}
}
- 题解2
import java.io.*;
import java.util.*;
public class Main {
public static final int N = 1010;
public static int[] V = new int[N];
public static int[] W = new int[N];
//只从前i个物品中选,选出来的物品的总体积小于等于j,满足这两个条件的价值的最大值
public static int[][] f = new int[N][N];
public static int n, m;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));
String[] str1 = br.readLine().split(" ");
n = Integer.parseInt(str1[0]);
m = Integer.parseInt(str1[1]);
for (int i = 1; i <= n; i++) {
String[] str2 = br.readLine().split(" ");
V[i] = Integer.parseInt(str2[0]);
W[i] = Integer.parseInt(str2[1]);
}
//初始化f[0][0 ~ m]都是0,所以省略
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]);
}
}
}
pw.println(f[n][m]);
br.close();
pw.close();
}
}
- 题解3
import java.io.*;
public class Main {
public static final int N = 1010;
public static int[] V = new int[N];
public static int[] W = new int[N];
//只从前i个物品中选,选出来的物品的总体积小于等于j,满足这两个条件的价值的最大值
public static int[] f = new int[N];
public static int n, m;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));
String[] str1 = br.readLine().split(" ");
n = Integer.parseInt(str1[0]);
m = Integer.parseInt(str1[1]);
for (int i = 1; i <= n; i++) {
String[] str2 = br.readLine().split(" ");
V[i] = Integer.parseInt(str2[0]);
W[i] = Integer.parseInt(str2[1]);
}
//初始化f[0][0 ~ m]都是0,所以省略
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]);
}
}
pw.println(f[m]);
br.close();
pw.close();
}
}