题目
有 N 种物品和一个容量是 V 的背包。
第 i 种物品最多有 si 件,每件体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。
输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。
接下来有 N 行,每行三个整数 vi,wi,si 用空格隔开,分别表示第 i 种物品的体积、价值和数量。
输出格式
输出一个整数,表示最大价值。
数据范围
0<N≤10000<N≤1000
0<V≤20000<V≤2000
0<vi,wi,si≤20000<vi,wi,si≤2000
提示:
本题考查多重背包的二进制优化方法。(与多重背包问题I不同的就是数据的范围发生了变化)
代码
import java.util.Scanner;
public class _多重背包问题II {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int N=scanner.nextInt();
int V=scanner.nextInt();
int[] dp=new int[V+1];
for (int i = 1; i <=N ; i++) {
int v=scanner.nextInt();
int w=scanner.nextInt();
int s=scanner.nextInt();
for (int j = 1; j <=s ; j<<=1) {
for (int k = V; k >=v*j ; k--) {
dp[k]=Math.max(dp[k],dp[k-v*j]+w*j);
}
s-=j;
}
if(s>0){
for (int k = V; k >=v*s; k--) {
dp[k]=Math.max(dp[k],dp[k-v*s]+w*s);
}
}
}
System.out.println(dp[V]);
scanner.close();
}
}
详解
为了进一步优化这个问题,我们可以使用二进制拆分法。这种方法的基本思想是将每种物品的数量拆分成若干个2的幂次的和,从而将多重背包问题转化为01背包问题。
具体步骤如下:
- 对于每种物品,计算最大的2的幂次
k
,使得2^k <= s[i]
。 - 将这种物品拆分成若干个新的物品,每个新物品的数量为
2^k
,体积为v[i]*2^k
,价值为w[i]*2^k
。 - 对于剩余的数量
s[i] - (2^k + 2^(k-1) + ... + 2^0)
(不是2的幂次的数量),将它作为一个新的物品,数量为s[i] - (2^k + 2^(k-1) + ... + 2^0)
,体积为v[i]
乘以这个数量,价值为w[i]
乘以这个数量。 - 使用01背包问题的动态规划解法求解这些新的物品。
这样,每种物品都被拆分成若干个新的物品,每个新物品的数量都是2的幂次,可以直接使用01背包问题的解法来求解。时间复杂度降为O(NVlogS)
,其中logS
是物品最大数量s[i]
的二进制位数。