特征:每个物品的个数有限
集合划分跟完全背包一样,只不过k
有上限s[i]
一、暴力解法
import java.util.Scanner;
public class Main{
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int N = in.nextInt();
int V = in.nextInt();
int[] v = new int[105];
int[] w = new int[105];
int[] s = new int[105];
int[][] f = new int[105][105];
for (int i = 1; i <= N; i++) {
v[i] = in.nextInt();
w[i] = in.nextInt();
s[i] = in.nextInt();
}
// 时间复杂度为NVS
for (int i = 1; i <= N; i++) {
for (int j = 0; j <= V; j++) {
for (int k = 0; k <= s[i] && k * v[i] <= j; k++) {
f[i][j]=Math.max(f[i][j],f[i - 1][j-k*v[i]]+k*w[i]);
}
}
}
System.out.println(f[N][V]);
}
}
二、与完全背包优化的区别
完全背包是求前缀的最大值,而多重背包是求固定长度的窗口内的最大值
// 完全背包中 k 无上限
f[i][j] = max(f[i-1][j], f[i-1][j-v]+w, f[i-1][j-2*v]+2*w,...f[i-1][j-k*v]+k*w)
f[i][j-v] = max(f[i-1][j-v], f[i-1][j-2*v]+w, f[i-1][j-3*v]+2*w,...f[i-1][j-k*v]+k*w)
k无上限所以直接用f[i][j-v]
替代 f[i][j]
后面的部分
但多重背包问题中每个物品的上限为s[i]
// 多重背包问题中每个物品的上限为s[i],用s表示
f[i][j] = max(f[i-1][j], f[i-1][j-v]+w, f[i-1][j-2*v]+2*w,...f[i-1][j-s*v]+s*w)
f[i][j-v] = max(f[i-1][j-v], f[i-1][j-2*v]+w,...f[i-1][j-s*v]+(s-1)*w + f[i-1][j-(s+1)*v]+s*w)
可以看出f[i][j-v]
比f[i][j]
多了一项 f[i-1][j-(s+1)*v]+s*w
,这一项无法去除,故无法用完全背包的优化思路
三、二进制优化思路
1 、需要明白一个前提
我们知道任意一个实数可以由二进制数来表示,也就是20~2k其中一项或几项的和。
这个前提其实就是二进制转化成十进制的方法,1110 (2) => 23+22+21+0*20=14
反过来就是 14 可以用 23、22、21这三个数字组成
2、针对题目
所以每个物品的s[i]
可以用若干堆2的幂次方个数来表示,以此可优化为01背包问题
但与二进制表示数不同的是此题中的优化必须是按顺序来的,即:20+21+22+23...2k
二进制表示中是不按顺序的
比如:
10 在二进制可以表示为 23+21
此题中如果10个物品则只能表达为 20 + 21 + 22 + 3
堆数为log210的上取整=4堆
即如果s不是正好可以分为每个组都是2的幂个的
那么就可以划分成20+21+22+23...2k +c = 2k+1-1 +c = s``c < 2k+1
下图为转化过程
原为 3个物品,每个物品个数为s
的多重背包问题,二进制优化后转换成了 有log2SA+log2SB+log2SC(均为上取整)个物品,每个物品个数为1
的01背包问题。
因为优化物品数量 发生变化,所以 v[]、w[] 数组开辟的空间也需要发生改变,之前是v[N]
,现在是v[log2SA+log2SB+log2SC(均为上取整)]
例如:
优化前开辟v[1005] w[1005] s[1005],优化后开辟N*log si
,log2000上取整是11,故应开辟1000*11+5(+5防越界),故开辟v[11005]
四、优化后代码
时间复杂度由NVS变为Nlog2S *V=NVlogS
import java.util.Scanner;
public class Main{
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int N = in.nextInt();
int V = in.nextInt();
int[] v = new int[11005];
int[] w = new int[11005];
int[] f = new int[11005];
int cnt = 0;
for (int i = 1; i <= N; i++) {
int a,b,s;
a = in.nextInt();
b = in.nextInt();
s = in.nextInt();
int k = 1;
while (k <= s) {
cnt ++;
v[cnt] = a * k;
w[cnt] = b * k;
s -= k;
k *= 2;
}
if (s > 0) {
cnt++;
v[cnt] = a * s;
w[cnt] = b * s;
}
}
N = cnt;
for (int i = 1; i <= N; i++) {
for (int j = V; j >= v[i]; j--) {
f[j] = Math.max(f[j],f[j -v[i]] + w[i]);
}
}
System.out.println(f[V]);
}
}