背包问题_3.pdf

Acwing 3. 完全背包问题

完全背包,不超过,最大价值
有 N 种物品和一个容量是 V 的背包,每种物品都有无限件可用。
第 i 种物品的体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。
输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。
接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 种物品的体积和价值。
输出格式
输出一个整数,表示最大价值。
数据范围
00输入样例

  1. 4 5
  2. 1 2
  3. 2 4
  4. 3 4
  5. 4 5

输出样例:

  1. 10

二维版本

  1. import java.util.*;
  2. public class Main {
  3. public static void main(String[] args) {
  4. Scanner sc = new Scanner(System.in);
  5. int n = sc.nextInt(), m = sc.nextInt();
  6. int[][] f = new int[n + 1][m + 1];
  7. for (int i = 1; i <= n; i++) {
  8. int v = sc.nextInt(), w = sc.nextInt();
  9. for (int j = 0; j <= m; j++) {
  10. for (int k = 0; k * v <= j; k++)
  11. f[i][j] = Math.max(f[i][j], f[i - 1][j - k * v] + k * w);
  12. }
  13. }
  14. System.out.println(f[n][m]);
  15. }
  16. }

优化掉枚举**k**的过程

  1. f[i, j] 表示只从前i个物品中选,且总体积不超过v的所有选法的集合,其属性是价值最大值
  2. 状态计算:只考虑前i件物品的情况下计算在所有体积下的选法的最大价值

f[i, j] = max(f[i - 1][j], f[i, j - v[i]] + w[i]

  1. 初始状态

i == 0时,意思是从前0件物品中选且总体积不超过j的最大价值,很显然,结果是0
Arrays.fill(f[0], 0) 可写可不写。

  1. import java.util.*;
  2. public class Main {
  3. public static void main(String[] args) {
  4. Scanner sc = new Scanner(System.in);
  5. int n = sc.nextInt(), m = sc.nextInt();
  6. int[][] f = new int[n + 1][m + 1];
  7. for (int i = 1; i <= n; i++) {
  8. int v = sc.nextInt(), w = sc.nextInt();
  9. for (int j = 0; j <= m; j++) {
  10. f[i][j] = f[i - 1][j];
  11. if (j >= v)
  12. f[i][j] = Math.max(f[i][j], f[i][j - v] + w);
  13. }
  14. }
  15. System.out.println(f[n][m]);
  16. }
  17. }

一维优化

带枚举物品数的遍历,j得从大到小遍历

  1. import java.util.*;
  2. public class Main {
  3. static final int N = 1010;
  4. static int[] f = new int[N];
  5. static int n, m;
  6. public static void main(String[] args) {
  7. Scanner sc = new Scanner(System.in);
  8. n = sc.nextInt();
  9. m = sc.nextInt();
  10. for (int i = 1; i <= n; i++) {
  11. int v = sc.nextInt(), w = sc.nextInt();
  12. for (int j = m; j >= v; j--) {
  13. for (int k = 0; k * v <= j; k++) {
  14. f[j] = Math.max(f[j], f[j - k * v] + k * w);
  15. }
  16. }
  17. }
  18. System.out.println(f[m]);
  19. }
  20. }

不带枚举物品数的遍历,与01背包正好相反,体积j从小到大枚举

  1. import java.util.*;
  2. public class Main {
  3. public static void main(String[] args) {
  4. Scanner sc = new Scanner(System.in);
  5. int n = sc.nextInt(), m = sc.nextInt();
  6. int[] f = new int[m + 1];
  7. for (int i = 1; i <= n; i++) {
  8. int v = sc.nextInt(), w = sc.nextInt();
  9. for (int j = v; j <= m; j++)
  10. f[j] = Math.max(f[j], f[j - v] + w);
  11. }
  12. System.out.println(f[m]);
  13. }
  14. }

完全背包求方案数

AcWing 1023. 买书
完全背包,恰好装满,方案数
思路:
状态表示:f[i][j]表示从前i个物品中选,总价格恰好为j的所有选法的集和
状态属性:集和元素个数
状态转移:考虑第i个数选或不选,如果选的话,选几个
f[i][j] += f[i - 1][j] + f[i][j - v]
初始化:f[0][0] = 1表示什么也不选也是一种方案

  1. import java.util.*;
  2. public class Main {
  3. static final int N = 4, M = 10010;
  4. static int[][] f = new int[N + 1][M];
  5. static int[] a = {0, 10, 20, 50, 100};
  6. public static void main(String[] args) {
  7. var sc = new Scanner(System.in);
  8. int m = sc.nextInt();
  9. f[0][0] = 1;
  10. for (int i = 1; i <= 4; i++) {
  11. int x = a[i];
  12. for (int j = 0; j <= m; j++) {
  13. f[i][j] = f[i - 1][j];
  14. if (j >= x)
  15. f[i][j] += f[i][j - x];
  16. }
  17. }
  18. System.out.println(f[N][m]);
  19. }
  20. }
  1. // 优化掉物品维
  2. import java.util.*;
  3. public class Main {
  4. static int N = 4, M = 1010;
  5. static int[] f = new int[M];
  6. static int m;
  7. static int[] a = new int[]{0, 10, 20, 50, 100};
  8. public static void main(String[] args) {
  9. Scanner sc = new Scanner(System.in);
  10. m = sc.nextInt();
  11. f[0] = 1;
  12. for (int i = 1; i <= N; i++) {
  13. int x = a[i];
  14. for (int j = x; j <= m; j++)
  15. f[j] += f[j - x];
  16. }
  17. System.out.println(f[m]);
  18. }
  19. }

其它例题

322. 零钱兑换 完全背包,恰好装满,最小代价
518. 零钱兑换 II 完全背包,恰好装满,方案数
等价于AcWing 1023. 买书