多重背包Ⅰ
问题描述
Acwing 4. 多重背包问题I
多重背包,每件物品数量有效,不超过,最大价值
有 N 种物品和一个容量是 V 的背包。
第 i 种物品最多有 si 件,每件体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。
输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。
接下来有 N 行,每行三个整数 vi,wi,si,用空格隔开,分别表示第 i 种物品的体积、价值和数量。
输出格式
输出一个整数,表示最大价值。
数据范围
0
4 51 2 32 4 13 4 34 5 2
输出样例:
10
二维版
与最裸的完全背包基本一致!多了物品数的判断
import java.util.*;public class Main {static final int N = 110, M = 110;static int[][] f = new int[N][M];static int n, m;public static void main(String[] args) {Scanner sc = new Scanner(System.in);n = sc.nextInt();m = sc.nextInt();// 遍历物品for (int i = 1; i <= n; i++) {int v = sc.nextInt(), w = sc.nextInt(), c = sc.nextInt();// 遍历体积for (int j = 0; j <= m; j++) {// 遍历决策for (int k = 0; k <= c && k * v <= j; k++) {f[i][j] = Math.max(f[i][j], f[i - 1][j - k * v] + k * w);}}}System.out.println(f[n][m]);}}
一维版
与不去掉枚举物品数的一维版本完全背包基本一致!
import java.util.*;public class Main {static final int N = 110, M = 110;static int[] f = new int[M];static int n, m;public static void main(String[] args) {Scanner sc = new Scanner(System.in);n = sc.nextInt();m = sc.nextInt();for (int i = 1; i <= n; i++) {int v = sc.nextInt(), w = sc.nextInt(), c = sc.nextInt();for (int j = m; j >= v; j--) {for (int k = 0; k <= c && k * v <= j; k++)f[j] = Math.max(f[j], f[j - k * v] + k * w);}}System.out.println(f[m]);}}
多重背包 Ⅱ
问题描述
Acwing 5. 多重背包问题 II
有 N 种物品和一个容量是 V 的背包。
第 i 种物品最多有 si 件,每件体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。
输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。
接下来有 N 行,每行三个整数 vi,wi,si,用空格隔开,分别表示第 i 种物品的体积、价值和数量。
输出格式
输出一个整数,表示最大价值。
数据范围
0
提示:
本题考查多重背包的二进制优化方法。
输入样例
4 51 2 32 4 13 4 34 5 2
输出样例:
10
时间复杂度:O(NVlogS)
二维版
import java.util.*;public class Main {static int N = 1010, M = 2010;static int[][] f = new int[N][M];static int n, m;public static void main(String[] args) {Scanner sc = new Scanner(System.in);n = sc.nextInt();m = sc.nextInt();for (int i = 1; i <= n; i++) {int v = sc.nextInt(), w = sc.nextInt(), s = sc.nextInt();for (int j = 0; j <= m; j++)f[i][j] = f[i - 1][j];for (int k = 1; k <= s; k <<= 1) {for (int j = m; j >= k * v; j--) {f[i][j] = Math.max(f[i][j], f[i][j - k * v] + k * w);}s -= k;}if (s > 0) {for (int j = m; j >= s * v; j--) {f[i][j] = Math.max(f[i][j], f[i][j - s * v] + s * w);}}}System.out.println(f[n][m]);}}
一维版
直接去掉物品维即可
import java.util.*;public class Main {static int N = 1010, M = 2010;static int[] f = new int[M];static int n, m;public static void main(String[] args) {Scanner sc = new Scanner(System.in);n = sc.nextInt();m = sc.nextInt();for (int i = 1; i <= n; i++) {int v = sc.nextInt(), w = sc.nextInt(), s = sc.nextInt();for (int k = 1; k <= s; k <<= 1) {for (int j = m; j >= k * v; j--)f[j] = Math.max(f[j], f[j - k * v] + k * w);s -= k;}if (s > 0) {for (int j = m; j >= s * v; j--)f[j] = Math.max(f[j], f[j - s * v] + s * w);}}System.out.println(f[m]);}}
// 另一种写法import java.util.*;public class Main {static final int N = 1010, M = 2010;static int[] V = new int[30], W = new int[30];static int n, m;static int[] f = new int[M];public static void main(String[] args) {Scanner sc = new Scanner(System.in);n = sc.nextInt();m = sc.nextInt();for (int i = 1; i <= n; i++) {int v = sc.nextInt(), w = sc.nextInt(), c = sc.nextInt();int idx = 1, p = 1;while (c >= p) {c -= p;V[idx] = v * p;W[idx] = w * p;idx++;p <<= 1;}if (c > 0) {V[idx] = v * c;W[idx] = w * c;idx++;}for (int k = 0; k < idx; k++)for (int j = m; j >= V[k]; j--)f[j] = Math.max(f[j], f[j - V[k]] + W[k]);}System.out.println(f[m]);}}
多重背包 Ⅲ
问题描述
6. 多重背包问题 III
有 N 种物品和一个容量是 V 的背包。
第 i 种物品最多有 si 件,每件体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。
输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。
接下来有 N 行,每行三个整数 vi,wi,si,用空格隔开,分别表示第 i 种物品的体积、价值和数量。
输出格式
输出一个整数,表示最大价值。
数据范围
0
提示
本题考查多重背包的单调队列优化方法。
输入样例
4 51 2 32 4 13 4 34 5 2
输出样例:
10
二维版
import java.util.*;public class Main {static final int N = 1010, M = 20010;static int[][] f = new int[N][M];static int[] q = new int[M];static int n, m;public static void main(String[] args) {Scanner sc = new Scanner(System.in);n = sc.nextInt();m = sc.nextInt();int hh, tt;for (int i = 1; i <= n; i++) {int v = sc.nextInt(), w = sc.nextInt(), s = sc.nextInt();for (int k = 0; k < v; k++) {hh = 0; tt = -1;for (int j = k; j <= m; j += v) {if (hh <= tt && q[hh] < j - s * v)hh++;while (hh <= tt && f[i-1][q[tt]] - (q[tt]-k) / v * w <= f[i-1][j] - (j-k) / v * w)tt--;q[++tt] = j;f[i][j] = f[i - 1][q[hh]] + (j - q[hh]) / v * w;}}}System.out.println(f[n][m]);}}
滚动数组优化
// 滚动数组版本import java.util.*;public class Main {static final int N = 1010, M = 20010;static int[][] f = new int[2][M];static int n, m;static int[] q = new int[M];public static void main(String[] args) {Scanner sc = new Scanner(System.in);n = sc.nextInt();m = sc.nextInt();int hh, tt;for (int i = 1; i <= n; i++) {int v = sc.nextInt(), w = sc.nextInt(), s = sc.nextInt();for (int k = 0; k < v; k++) {hh = 0; tt = -1;for (int j = k; j <= m; j += v) {if (hh <= tt && q[hh] < j - s * v)hh++;while (hh <= tt && f[i-1&1][q[tt]] - (q[tt] - k) / v * w <= f[i-1&1][j] - (j - k) / v * w)tt--;q[++tt] = j;f[i&1][j] = f[i-1&1][q[hh]] + (j - q[hh]) / v * w;}}}System.out.println(f[n&1][m]);}}// 数组复制版本import java.util.*;public class Main {static final int N = 1010, M = 20010;static int[] f = new int[M], g = new int[M];static int n, m;static int[] q = new int[M];public static void main(String[] args) {Scanner sc = new Scanner(System.in);n = sc.nextInt();m = sc.nextInt();int hh, tt;for (int i = 1; i <= n; i++) {int v = sc.nextInt(), w = sc.nextInt(), s = sc.nextInt();System.arraycopy(f, 0, g, 0, m + 1);for (int k = 0; k < v; k++) {hh = 0; tt = -1;for (int j = k; j <= m; j += v) {if (hh <= tt && q[hh] < j - s * v)hh++;while (hh <= tt && g[q[tt]] - (q[tt] - k) / v * w <= g[j] - (j - k) / v * w)tt--;q[++tt] = j;f[j] = g[q[hh]] + (j - q[hh]) / v * w;}}}System.out.println(f[m]);}}
// 20220407 更新import java.util.*;public class Main {static final int N = 1010, M = 20010;static int[][] f = new int[N][M];static int n, m;static int[] q = new int[M];public static void main(String[] args) {Scanner sc = new Scanner(System.in);n = sc.nextInt();m = sc.nextInt();for (int i = 1; i <= n; i++) {int v = sc.nextInt(), w = sc.nextInt(), s = sc.nextInt();for (int k = 0; k < v; k++) {int hh = 0, tt = -1;for (int j = k; j <= m; j += v) {if (hh <= tt && q[hh] < j - s * v)hh++;// 这里的判断做了一点修改,本质一样,但更好写了。while (hh <= tt && f[i-1][j] >= f[i-1][q[tt]] + (j-q[tt]) / v * w)tt--;q[++tt] = j;f[i][j] = f[i - 1][q[hh]] + (j - q[hh]) / v * w;}}}System.out.println(f[n][m]);}}
基础
Acwing 154. 滑动窗口
其它例题
AcWing 1019. 庆功会
多重背包,不超过,最大价值
多重背包求方案数
AcWing 451. 摆花
多重背包,恰好,方案数
多重背包Ⅰ
// 最暴力的写法import java.util.*;public class Main {static final int N = 110, M = 110, MOD = 1000007;static int[][] f = new int[N][M];static int n, m;public static void main(String[] args) {Scanner sc = new Scanner(System.in);n = sc.nextInt();m = sc.nextInt();f[0][0] = 1;for (int i = 1; i <= n; i++) {int s = sc.nextInt();for (int j = 0; j <= m; j++) {for (int k = 0; k <= s && k <= j; k++)f[i][j] = (f[i][j] + f[i - 1][j - k]) % MOD;}}System.out.println(f[n][m]);}}
// 优化掉物品维import java.util.*;public class Main {static final int N = 110, M = 110, MOD = 1000007;static int[] f = new int[M];static int n, m;public static void main(String[] args) {Scanner sc = new Scanner(System.in);n = sc.nextInt();m = sc.nextInt();f[0] = 1;for (int i = 1; i <= n; i++) {int s = sc.nextInt();for (int j = m; j >= 1; j--) {for (int k = 1; k <= s && k <= j; k++)f[j] = (f[j] + f[j - k]) % MOD;}}System.out.println(f[m]);}}
多重背包Ⅱ
无法求解
例如一个物品个数为2,正常结果为f[1][1] = 1,但是使用二进制优化的结果是f[1][1] = 2
多重背包Ⅲ
不需要单调队列,用一个前缀和(滑动窗口)即可。
// 二维版本import java.util.*;public class Main {static final int N = 110, M = 110, MOD = 1000007;static int[][] f = new int[N][M];static int n, m;public static void main(String[] args) {Scanner sc = new Scanner(System.in);n = sc.nextInt();m = sc.nextInt();f[0][0] = 1;for (int i = 1; i <= n; i++) {int s = sc.nextInt();int cnt = 0;for (int j = 0; j <= m; j++) {cnt = (cnt + f[i - 1][j]) % MOD;f[i][j] = cnt;if (j >= s)cnt = (cnt - f[i - 1][j - s] + MOD) % MOD;}}System.out.println(f[n][m]);}}
// 一维版本import java.util.*;public class Main {static final int N = 110, M = 110, MOD = 1000007;static int[] f = new int[M], g = new int[M];static int n, m;public static void main(String[] args) {Scanner sc = new Scanner(System.in);n = sc.nextInt();m = sc.nextInt();f[0] = 1;for (int i = 1; i <= n; i++) {int s = sc.nextInt();System.arraycopy(f, 0, g, 0, m + 1);int cnt = 0;for (int j = 0; j <= m; j++) {cnt = (cnt + g[j]) % MOD;f[j] = cnt;if (j >= s)cnt = (cnt - g[j - s] + MOD) % MOD;}}System.out.println(f[m]);}}
LCP 25. 古董键盘
多重背包,每组可选[0, k]件物品,恰好选n件,排列数
相较于摆花那道题,没有规定具体排列方式
