多重背包Ⅰ
问题描述
Acwing 4. 多重背包问题I
多重背包,每件物品数量有效,不超过,最大价值
有 N 种物品和一个容量是 V 的背包。
第 i 种物品最多有 si 件,每件体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。
输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。
接下来有 N 行,每行三个整数 vi,wi,si,用空格隔开,分别表示第 i 种物品的体积、价值和数量。
输出格式
输出一个整数,表示最大价值。
数据范围
0
4 5
1 2 3
2 4 1
3 4 3
4 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 5
1 2 3
2 4 1
3 4 3
4 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 5
1 2 3
2 4 1
3 4 3
4 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
件,排列数
相较于摆花那道题,没有规定具体排列方式