背包问题_12.pdf

AcWing 11. 背包问题求方案数

01背包,不超过,得到最大价值的所有方案的数量
题目描述
有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。
第 i 件物品的体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出 最优选法的方案数。注意答案可能很大,请输出答案模 109+7 的结果。

输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。
接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 件物品的体积和价值。

输出格式
输出一个整数,表示 方案数 模 109+7 的结果。

数据范围
00<vi,wi≤1000

样例
输入样例
4 5
1 2
2 4
3 4
4 6
输出样例:
2

思路:
两种状态定义方式:“恰好”和“不超过”

  • 恰好:
    • f[i][j]表示从前i个物品中选且总体积恰好为j的获得最大价值的选法的集和,属性是最大价值
    • g[i][j]表示从前i个物品中选且总价值为f[i][j]的方案的集和,属性是方案数
    • 初始化:f[i][j] = -INF, f[0][0] = 0, g[0][0] = 1
  • 不超过:
    • f[i][j]表示从前i个物品中选且总体积不超过j的获得最大价值的选法的集和,属性是最大价值
    • g[i][j]表示从前i个物品中选且总价值为f[i][j]的方案的集和,属性是方案数。
    • 初始化:f[i][j] = -INF, f[0][j] = 0, g[0][j] = 1
  1. // 恰好
  2. import java.util.*;
  3. public class Main {
  4. static final int N = 1010, INF = 0x3f3f3f3f, MOD = (int)(1e9 + 7);
  5. static int[][] f = new int[N][N], g = new int[N][N];
  6. static int n, m;
  7. public static void main(String[] args) {
  8. Scanner sc = new Scanner(System.in);
  9. n = sc.nextInt();
  10. m = sc.nextInt();
  11. for (int i = 0; i <= n; i++) {
  12. Arrays.fill(f[i], -INF);
  13. }
  14. f[0][0] = 0;
  15. g[0][0] = 1;
  16. for (int i = 1; i <= n; i++) {
  17. int v = sc.nextInt(), w = sc.nextInt();
  18. for (int j = 0; j <= m; j++) {
  19. if (j >= v) {
  20. int max = Math.max(f[i - 1][j], f[i - 1][j - v] + w);
  21. int cnt = 0;
  22. if (f[i - 1][j] == max) cnt += g[i - 1][j];
  23. if (f[i - 1][j - v] + w == max) cnt += g[i - 1][j - v];
  24. f[i][j] = max;
  25. g[i][j] = cnt % MOD;
  26. } else {
  27. f[i][j] = f[i - 1][j];
  28. g[i][j] = g[i - 1][j];
  29. }
  30. }
  31. }
  32. int res = 0;
  33. for (int i = 0; i <= m; i++)
  34. res = Math.max(res, f[n][i]);
  35. int cnt = 0;
  36. for (int i = 0; i <= m; i++)
  37. if (f[n][i] == res)
  38. cnt = (cnt + g[n][i]) % MOD;
  39. System.out.println(cnt);
  40. }
  41. }
  42. // 一维
  43. import java.util.*;
  44. public class Main {
  45. static final int N = 1010, INF = 0x3f3f3f3f, MOD = (int)(1e9 + 7);
  46. static int[] f = new int[N], g = new int[N];
  47. static int n, m;
  48. public static void main(String[] args) {
  49. Scanner sc = new Scanner(System.in);
  50. n = sc.nextInt();
  51. m = sc.nextInt();
  52. Arrays.fill(f, -INF);
  53. f[0] = 0;
  54. g[0] = 1;
  55. for (int i = 1; i <= n; i++) {
  56. int v = sc.nextInt(), w = sc.nextInt();
  57. for (int j = m; j >= v; j--) {
  58. int max = Math.max(f[j], f[j - v] + w);
  59. int cnt = 0;
  60. if (max == f[j]) cnt += g[j];
  61. if (max == f[j - v] + w) cnt += g[j - v];
  62. f[j] = max;
  63. g[j] = cnt % MOD;
  64. }
  65. }
  66. int res = 0;
  67. for (int i = 0; i <= m; i++)
  68. res = Math.max(res, f[i]);
  69. int cnt = 0;
  70. for (int i = 0; i <= m; i++)
  71. if (f[i] == res)
  72. cnt = (cnt + g[i]) % MOD;
  73. System.out.println(cnt);
  74. }
  75. }
  1. // 不超过
  2. import java.util.*;
  3. public class Main {
  4. static final int N = 1010, INF = 0x3f3f3f3f, MOD = (int)(1e9 + 7);
  5. static int[][] f = new int[N][N], g = new int[N][N];
  6. static int n, m;
  7. public static void main(String[] args) {
  8. Scanner sc = new Scanner(System.in);
  9. n = sc.nextInt();
  10. m = sc.nextInt();
  11. for (int i = 1; i <= n; i++)
  12. Arrays.fill(f[i], -INF);
  13. Arrays.fill(g[0], 1);
  14. for (int i = 1; i <= n; i++) {
  15. int v = sc.nextInt(), w = sc.nextInt();
  16. for (int j = 0; j <= m; j++) {
  17. if (j >= v) {
  18. int max = Math.max(f[i - 1][j], f[i - 1][j - v] + w);
  19. int cnt = 0;
  20. if (max == f[i - 1][j]) cnt += g[i - 1][j];
  21. if (max == f[i - 1][j - v] + w) cnt += g[i - 1][j - v];
  22. f[i][j] = max;
  23. g[i][j] = cnt % MOD;
  24. } else {
  25. f[i][j] = f[i - 1][j];
  26. g[i][j] = g[i - 1][j];
  27. }
  28. }
  29. }
  30. System.out.println(g[n][m]);
  31. }
  32. }
  33. // 一维
  34. import java.util.*;
  35. public class Main {
  36. static final int N = 1010, INF = 0x3f3f3f3f, MOD = (int)(1e9 + 7);
  37. static int[] f = new int[N], g = new int[N];
  38. static int n, m;
  39. public static void main(String[] args) {
  40. Scanner sc = new Scanner(System.in);
  41. n = sc.nextInt();
  42. m = sc.nextInt();
  43. Arrays.fill(g, 1);
  44. for (int i = 1; i <= n; i++) {
  45. int v = sc.nextInt(), w = sc.nextInt();
  46. for (int j = m; j >= v; j--) {
  47. int max = Math.max(f[j], f[j - v] + w);
  48. int cnt = 0;
  49. if (max == f[j]) cnt += g[j];
  50. if (max == f[j - v] + w) cnt += g[j - v];
  51. f[j] = max;
  52. g[j] = cnt % MOD;
  53. }
  54. }
  55. System.out.println(g[m]);
  56. }
  57. }

方法2:
不用g, 用dfs考虑方案的数量
从终止状态f[n][m]倒推至f[0][0]
即求dfs(n m),在深搜的过程中计算方案数

结果会TLE,因为物品数太多了,深搜时间复杂度过高了