1 01背包
1.1 题目
有N 件物品和一个容量为V 的背包。放入第i 件物品耗费的费用是Ci,得到的价值是Wi。求解将哪些物品装入背包可使价值总和最大。
1.2 未优化版
1.2.1 代码
public static void main(String[] args) {//原始版
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();//n个物品
int m = scanner.nextInt();//容量是w
int[] w = new int[n + 5];//容量是wi
int[] v = new int[n + 5];//价值是vi
int[][] dp = new int[n + 2][m + 2];
for (int i = 1; i <= n; i++) {
v[i] = scanner.nextInt();
w[i] = scanner.nextInt();
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
dp[i][j] = dp[i - 1][j];
if (v[i] <= j) {
dp[i][j] = Math.max(dp[i - 1][j - v[i]] + w[i], dp[i][j]);
}
}
}
System.out.println(dp[n][m]);
}
1.2.2 时空复杂度
1.2.2.1 时间: n*m
1.2.2.2 空间: n*m
1.3 滚动数组优化
public static void main(String[] args) {//优化版
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();//n个物品
int m = scanner.nextInt();//容量是w
int[] w = new int[n + 5];//容量是wi
int[] v = new int[n + 5];//价值是vi
int[] dp = new int[m + 2];
for (int i = 1; i <= n; i++) {
v[i] = scanner.nextInt();
w[i] = scanner.nextInt();
}
for (int i = 1; i <= n; i++) {
for (int j = m; j >= v[i]; j--) {
dp[j] = Math.max(dp[j - v[i]] + w[i], dp[j]);
}
}
System.out.println(dp[m]);
}
1.3.2 时空复杂度
1.3.2.1 时间: n*m
1.3.2.2 空间: m
2 完全背包
2.1 题目
有N 种物品和一个容量为V 的背包,每种物品都有无限件可用。放入第i 种物品的费用是Ci,价值是Wi。求解:将哪些物品装入背包,可使这些物品的耗费的费用总和不超过背包容量,且价值总和最大。
2.2 未优化版
public static void main(String[] args) {//原始版
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int m = scanner.nextInt();
int[] v = new int[n + 1];
int[] w = new int[n + 1];
int[][] dp = new int[n + 1][m + 1];
for (int i = 1; i <= n; i++) {
v[i] = scanner.nextInt();
w[i] = scanner.nextInt();
}
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= m; j++) {
for (int k = 0; k <= n; k++) {
if (j - k * v[i] >= 0) {
dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - k * v[i]] + k * w[i]);
}
}
}
}
System.out.println(dp[n][m]);
}
2.2.1 时空复杂度
2.2.2.1 时间: n*m*m
2.2.2.2 空间: m*n
2.3 优化一层循环
public static void main(String[] args) {//优化版
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int m = scanner.nextInt();
int[] v = new int[n + 1];
int[] w = new int[n + 1];
int[][] dp = new int[n + 1][m + 1];
for (int i = 1; i <= n; i++) {
v[i] = scanner.nextInt();
w[i] = scanner.nextInt();
}
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= m; j++) {
dp[i][j] = dp[i - 1][j];
if (j - v[i] >= 0) {
dp[i][j] = Math.max(dp[i][j], dp[i][j - v[i]] + w[i]);
}
}
}
System.out.println(dp[n][m]);
}
2.3.1 时空复杂度
2.3.2.1 时间: n*m
2.3.2.2 空间: m*n
2.4 滚动数组优化
public static void main(String[] args) {//最终优化版
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int m = scanner.nextInt();
int[] v = new int[n + 1];
int[] w = new int[n + 1];
int[] dp = new int[m + 1];
for (int i = 1; i <= n; i++) {
v[i] = scanner.nextInt();
w[i] = scanner.nextInt();
}
for (int i = 1; i <= n; i++) {
for (int j = v[i]; j <= m; j++) {
dp[j] = Math.max(dp[j], dp[j - v[i]] + w[i]);
}
}
System.out.println(dp[m]);
}
2.4.1 时空复杂度
2.4.2.1 时间: n*m
2.4.2.2 空间: m
3 分组背包
3.1 题目
给你N组物品,然后每一组你至多选择一个物品(也可以不选),每个物品都有自己的体积和价值,现在给你一个容里为M的背包,让你用这个背包装物品,使得物品价值总和最大.
3.1 未优化版
public static void main(String[] args) {//未优化版
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int m = scanner.nextInt();
int[] s = new int[n + 1];
int[][] v = new int[n + 1][m + 1];
int[][] w = new int[n + 1][m + 1];
int[][] dp = new int[n + 1][m + 1];
for (int i = 1; i <= n; i++) {
s[i] = scanner.nextInt();
for (int j = 0; j < s[i]; j++) {
v[i][j] = scanner.nextInt();
w[i][j] = scanner.nextInt();
}
}
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= m; j++) {
for (int k = 0; k < s[i]; k++) {
dp[i][j] = dp[i - 1][j];
if (j - v[i][k] >= 0) {
dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - v[i][k]] + w[i][k]);
}
}
}
}
System.out.println(dp[n][m]);
}
2.3.1 时空复杂度
2.3.2.1 时间: n*m*s[i]
2.3.2.2 空间: m*n
3.2 优化版
public static void main(String[] args) {//优化版
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int m = scanner.nextInt();
int[] s = new int[n + 1];
int[][] v = new int[n + 1][m + 1];
int[][] w = new int[n + 1][m + 1];
int[] dp = new int[m + 1];
for (int i = 1; i <= n; i++) {
s[i] = scanner.nextInt();
for (int j = 0; j < s[i]; j++) {
v[i][j] = scanner.nextInt();
w[i][j] = scanner.nextInt();
}
}
for (int i = 1; i <= n; i++) {
for (int j = m; j >= 0; j--) {
for (int k = 0; k < s[i]; k++) {
if (j - v[i][k] >= 0) {
dp[j] = Math.max(dp[j], dp[j - v[i][k]] + w[i][k]);
}
}
}
}
System.out.println(dp[m]);
}
2.4.1 时空复杂度
2.4.2.1 时间: n*m*s[i]
2.4.2.2 空间: m
4 多重背包
4.1题目
有N 种物品和一个容量为V 的背包。第i 种物品最多有Mi 件可用,每件耗费的空间是vi,价值是Wi。求解将哪些物品装入背包可使这些物品的耗费的空间总和不超过背包容量,且价值总和最大。
4.1 未优化版
public static void main0(String[] args) {//原始版
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int m = scanner.nextInt();
int[][] dp = new int[n + 1][m + 1];
int[] v = new int[n + 1];
int[] w = new int[n + 1];
int[] s = new int[n + 1];
for (int i = 1; i <= n; i++) {
v[i] = scanner.nextInt();
w[i] = scanner.nextInt();
s[i] = scanner.nextInt();
}
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= m; j++) {
for (int k = 0; k <= s[i]; k++) {
dp[i][j] = dp[i - 1][j];
if (j >= v[i] * k) {
dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - k * v[i]] + k * w[i]);
}
}
}
}
System.out.println(dp[n][m]);
}
4.2 优化版
public static void main(String[] args) {//小优化
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int m = scanner.nextInt();
int[][] dp = new int[n + 1][m + 1];
int[] v = new int[n + 1];
int[] w = new int[n + 1];
int[] s = new int[n + 1];
for (int i = 1; i <= n; i++) {
v[i] = scanner.nextInt();
w[i] = scanner.nextInt();
s[i] = scanner.nextInt();
}
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= m; j++) {
for (int k = 0; k <= s[i] && k * v[i] <= j; k++) {//减少循环
dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - k * v[i]] + k * w[i]);
}
}
}
System.out.println(dp[n][m]);
}
4.3 二进制优化
public static void main(String[] args) {//二进制优化
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int m = scanner.nextInt();
int[] dp = new int[N];//由于需要扩大数组,所以开得比较大
int[] v = new int[N];
int[] w = new int[N];
int index = 1;
for (int i = 1; i <= n; i++) {
int a = scanner.nextInt();
int b = scanner.nextInt();
int s = scanner.nextInt();
int k = 1;
while (k <= s) {
v[index] = a * k;
w[index++] = b * k;
s -= k;
k <<= 2;
}
if (s > 0) {
v[index] = a * s;
w[index++] = b * s;
s = 0;
}
}
n=index;
for (int i = 1; i <= n; i++) {//01背包
for (int j = m; j >= v[i]; j--) {
dp[j] = Math.max(dp[j], dp[j - v[i]] + w[i]);
}
}
System.out.println(dp[m]);
}
5 总结
背包是一种动态规划问题。动态规划的核心就是状态转移方程