多重背包Ⅰ

背包问题_4.pdf

问题描述

Acwing 4. 多重背包问题I
多重背包,每件物品数量有效,不超过,最大价值
有 N 种物品和一个容量是 V 的背包。
第 i 种物品最多有 si 件,每件体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。
输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。
接下来有 N 行,每行三个整数 vi,wi,si,用空格隔开,分别表示第 i 种物品的体积、价值和数量。
输出格式
输出一个整数,表示最大价值。
数据范围
00输入样例

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

输出样例:

  1. 10

二维版

与最裸的完全背包基本一致!多了物品数的判断

  1. import java.util.*;
  2. public class Main {
  3. static final int N = 110, M = 110;
  4. static int[][] f = new int[N][M];
  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. // 遍历物品
  11. for (int i = 1; i <= n; i++) {
  12. int v = sc.nextInt(), w = sc.nextInt(), c = sc.nextInt();
  13. // 遍历体积
  14. for (int j = 0; j <= m; j++) {
  15. // 遍历决策
  16. for (int k = 0; k <= c && k * v <= j; k++) {
  17. f[i][j] = Math.max(f[i][j], f[i - 1][j - k * v] + k * w);
  18. }
  19. }
  20. }
  21. System.out.println(f[n][m]);
  22. }
  23. }

一维版

与不去掉枚举物品数的一维版本完全背包基本一致!

  1. import java.util.*;
  2. public class Main {
  3. static final int N = 110, M = 110;
  4. static int[] f = new int[M];
  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(), c = sc.nextInt();
  12. for (int j = m; j >= v; j--) {
  13. for (int k = 0; k <= c && k * v <= j; k++)
  14. f[j] = Math.max(f[j], f[j - k * v] + k * w);
  15. }
  16. }
  17. System.out.println(f[m]);
  18. }
  19. }

多重背包 Ⅱ

背包问题_5.pdf

问题描述

Acwing 5. 多重背包问题 II
有 N 种物品和一个容量是 V 的背包。
第 i 种物品最多有 si 件,每件体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。
输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。
接下来有 N 行,每行三个整数 vi,wi,si,用空格隔开,分别表示第 i 种物品的体积、价值和数量。
输出格式
输出一个整数,表示最大价值。
数据范围
000

提示:

本题考查多重背包的二进制优化方法。
输入样例

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

输出样例:

  1. 10

时间复杂度:O(NVlogS)

二维版

  1. import java.util.*;
  2. public class Main {
  3. static int N = 1010, M = 2010;
  4. static int[][] f = new int[N][M];
  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(), s = sc.nextInt();
  12. for (int j = 0; j <= m; j++)
  13. f[i][j] = f[i - 1][j];
  14. for (int k = 1; k <= s; k <<= 1) {
  15. for (int j = m; j >= k * v; j--) {
  16. f[i][j] = Math.max(f[i][j], f[i][j - k * v] + k * w);
  17. }
  18. s -= k;
  19. }
  20. if (s > 0) {
  21. for (int j = m; j >= s * v; j--) {
  22. f[i][j] = Math.max(f[i][j], f[i][j - s * v] + s * w);
  23. }
  24. }
  25. }
  26. System.out.println(f[n][m]);
  27. }
  28. }

一维版

直接去掉物品维即可

  1. import java.util.*;
  2. public class Main {
  3. static int N = 1010, M = 2010;
  4. static int[] f = new int[M];
  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(), s = sc.nextInt();
  12. for (int k = 1; k <= s; k <<= 1) {
  13. for (int j = m; j >= k * v; j--)
  14. f[j] = Math.max(f[j], f[j - k * v] + k * w);
  15. s -= k;
  16. }
  17. if (s > 0) {
  18. for (int j = m; j >= s * v; j--)
  19. f[j] = Math.max(f[j], f[j - s * v] + s * w);
  20. }
  21. }
  22. System.out.println(f[m]);
  23. }
  24. }
  1. // 另一种写法
  2. import java.util.*;
  3. public class Main {
  4. static final int N = 1010, M = 2010;
  5. static int[] V = new int[30], W = new int[30];
  6. static int n, m;
  7. static int[] f = new int[M];
  8. public static void main(String[] args) {
  9. Scanner sc = new Scanner(System.in);
  10. n = sc.nextInt();
  11. m = sc.nextInt();
  12. for (int i = 1; i <= n; i++) {
  13. int v = sc.nextInt(), w = sc.nextInt(), c = sc.nextInt();
  14. int idx = 1, p = 1;
  15. while (c >= p) {
  16. c -= p;
  17. V[idx] = v * p;
  18. W[idx] = w * p;
  19. idx++;
  20. p <<= 1;
  21. }
  22. if (c > 0) {
  23. V[idx] = v * c;
  24. W[idx] = w * c;
  25. idx++;
  26. }
  27. for (int k = 0; k < idx; k++)
  28. for (int j = m; j >= V[k]; j--)
  29. f[j] = Math.max(f[j], f[j - V[k]] + W[k]);
  30. }
  31. System.out.println(f[m]);
  32. }
  33. }

多重背包 Ⅲ

背包问题_6.pdf

问题描述

6. 多重背包问题 III
有 N 种物品和一个容量是 V 的背包。
第 i 种物品最多有 si 件,每件体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。
输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。
接下来有 N 行,每行三个整数 vi,wi,si,用空格隔开,分别表示第 i 种物品的体积、价值和数量。
输出格式
输出一个整数,表示最大价值。
数据范围
000

提示

本题考查多重背包的单调队列优化方法。
输入样例

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

输出样例:

  1. 10

二维版

  1. import java.util.*;
  2. public class Main {
  3. static final int N = 1010, M = 20010;
  4. static int[][] f = new int[N][M];
  5. static int[] q = new int[M];
  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. int hh, tt;
  12. for (int i = 1; i <= n; i++) {
  13. int v = sc.nextInt(), w = sc.nextInt(), s = sc.nextInt();
  14. for (int k = 0; k < v; k++) {
  15. hh = 0; tt = -1;
  16. for (int j = k; j <= m; j += v) {
  17. if (hh <= tt && q[hh] < j - s * v)
  18. hh++;
  19. while (hh <= tt && f[i-1][q[tt]] - (q[tt]-k) / v * w <= f[i-1][j] - (j-k) / v * w)
  20. tt--;
  21. q[++tt] = j;
  22. f[i][j] = f[i - 1][q[hh]] + (j - q[hh]) / v * w;
  23. }
  24. }
  25. }
  26. System.out.println(f[n][m]);
  27. }
  28. }

滚动数组优化

  1. // 滚动数组版本
  2. import java.util.*;
  3. public class Main {
  4. static final int N = 1010, M = 20010;
  5. static int[][] f = new int[2][M];
  6. static int n, m;
  7. static int[] q = new int[M];
  8. public static void main(String[] args) {
  9. Scanner sc = new Scanner(System.in);
  10. n = sc.nextInt();
  11. m = sc.nextInt();
  12. int hh, tt;
  13. for (int i = 1; i <= n; i++) {
  14. int v = sc.nextInt(), w = sc.nextInt(), s = sc.nextInt();
  15. for (int k = 0; k < v; k++) {
  16. hh = 0; tt = -1;
  17. for (int j = k; j <= m; j += v) {
  18. if (hh <= tt && q[hh] < j - s * v)
  19. hh++;
  20. while (hh <= tt && f[i-1&1][q[tt]] - (q[tt] - k) / v * w <= f[i-1&1][j] - (j - k) / v * w)
  21. tt--;
  22. q[++tt] = j;
  23. f[i&1][j] = f[i-1&1][q[hh]] + (j - q[hh]) / v * w;
  24. }
  25. }
  26. }
  27. System.out.println(f[n&1][m]);
  28. }
  29. }
  30. // 数组复制版本
  31. import java.util.*;
  32. public class Main {
  33. static final int N = 1010, M = 20010;
  34. static int[] f = new int[M], g = new int[M];
  35. static int n, m;
  36. static int[] q = new int[M];
  37. public static void main(String[] args) {
  38. Scanner sc = new Scanner(System.in);
  39. n = sc.nextInt();
  40. m = sc.nextInt();
  41. int hh, tt;
  42. for (int i = 1; i <= n; i++) {
  43. int v = sc.nextInt(), w = sc.nextInt(), s = sc.nextInt();
  44. System.arraycopy(f, 0, g, 0, m + 1);
  45. for (int k = 0; k < v; k++) {
  46. hh = 0; tt = -1;
  47. for (int j = k; j <= m; j += v) {
  48. if (hh <= tt && q[hh] < j - s * v)
  49. hh++;
  50. while (hh <= tt && g[q[tt]] - (q[tt] - k) / v * w <= g[j] - (j - k) / v * w)
  51. tt--;
  52. q[++tt] = j;
  53. f[j] = g[q[hh]] + (j - q[hh]) / v * w;
  54. }
  55. }
  56. }
  57. System.out.println(f[m]);
  58. }
  59. }
  1. // 20220407 更新
  2. import java.util.*;
  3. public class Main {
  4. static final int N = 1010, M = 20010;
  5. static int[][] f = new int[N][M];
  6. static int n, m;
  7. static int[] q = new int[M];
  8. public static void main(String[] args) {
  9. Scanner sc = new Scanner(System.in);
  10. n = sc.nextInt();
  11. m = sc.nextInt();
  12. for (int i = 1; i <= n; i++) {
  13. int v = sc.nextInt(), w = sc.nextInt(), s = sc.nextInt();
  14. for (int k = 0; k < v; k++) {
  15. int hh = 0, tt = -1;
  16. for (int j = k; j <= m; j += v) {
  17. if (hh <= tt && q[hh] < j - s * v)
  18. hh++;
  19. // 这里的判断做了一点修改,本质一样,但更好写了。
  20. while (hh <= tt && f[i-1][j] >= f[i-1][q[tt]] + (j-q[tt]) / v * w)
  21. tt--;
  22. q[++tt] = j;
  23. f[i][j] = f[i - 1][q[hh]] + (j - q[hh]) / v * w;
  24. }
  25. }
  26. }
  27. System.out.println(f[n][m]);
  28. }
  29. }

基础
Acwing 154. 滑动窗口
其它例题
AcWing 1019. 庆功会
多重背包,不超过,最大价值

多重背包求方案数

AcWing 451. 摆花
多重背包,恰好,方案数

多重背包Ⅰ

  1. // 最暴力的写法
  2. import java.util.*;
  3. public class Main {
  4. static final int N = 110, M = 110, MOD = 1000007;
  5. static int[][] f = new int[N][M];
  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. f[0][0] = 1;
  12. for (int i = 1; i <= n; i++) {
  13. int s = sc.nextInt();
  14. for (int j = 0; j <= m; j++) {
  15. for (int k = 0; k <= s && k <= j; k++)
  16. f[i][j] = (f[i][j] + f[i - 1][j - k]) % MOD;
  17. }
  18. }
  19. System.out.println(f[n][m]);
  20. }
  21. }
  1. // 优化掉物品维
  2. import java.util.*;
  3. public class Main {
  4. static final int N = 110, M = 110, MOD = 1000007;
  5. static int[] f = new int[M];
  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. f[0] = 1;
  12. for (int i = 1; i <= n; i++) {
  13. int s = sc.nextInt();
  14. for (int j = m; j >= 1; j--) {
  15. for (int k = 1; k <= s && k <= j; k++)
  16. f[j] = (f[j] + f[j - k]) % MOD;
  17. }
  18. }
  19. System.out.println(f[m]);
  20. }
  21. }

多重背包Ⅱ

无法求解
例如一个物品个数为2,正常结果为f[1][1] = 1,但是使用二进制优化的结果是f[1][1] = 2

多重背包Ⅲ

不需要单调队列,用一个前缀和(滑动窗口)即可。

  1. // 二维版本
  2. import java.util.*;
  3. public class Main {
  4. static final int N = 110, M = 110, MOD = 1000007;
  5. static int[][] f = new int[N][M];
  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. f[0][0] = 1;
  12. for (int i = 1; i <= n; i++) {
  13. int s = sc.nextInt();
  14. int cnt = 0;
  15. for (int j = 0; j <= m; j++) {
  16. cnt = (cnt + f[i - 1][j]) % MOD;
  17. f[i][j] = cnt;
  18. if (j >= s)
  19. cnt = (cnt - f[i - 1][j - s] + MOD) % MOD;
  20. }
  21. }
  22. System.out.println(f[n][m]);
  23. }
  24. }
  1. // 一维版本
  2. import java.util.*;
  3. public class Main {
  4. static final int N = 110, M = 110, MOD = 1000007;
  5. static int[] f = new int[M], g = new int[M];
  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. f[0] = 1;
  12. for (int i = 1; i <= n; i++) {
  13. int s = sc.nextInt();
  14. System.arraycopy(f, 0, g, 0, m + 1);
  15. int cnt = 0;
  16. for (int j = 0; j <= m; j++) {
  17. cnt = (cnt + g[j]) % MOD;
  18. f[j] = cnt;
  19. if (j >= s)
  20. cnt = (cnt - g[j - s] + MOD) % MOD;
  21. }
  22. }
  23. System.out.println(f[m]);
  24. }
  25. }

LCP 25. 古董键盘
多重背包,每组可选[0, k]件物品,恰好选n件,排列数
相较于摆花那道题,没有规定具体排列方式