1.题目
2. 基本思想
第 i 个物品最多选 s 个,可以表示为 i 个物品1个、2个、4个、8个、16个直到小于s的2的幂次,由此转换成 0 -1背包问题。
通过多个背包某个物品 s 次 ,转换为 logS 的 0-1背包问题,可以拼凑出问题的解。
3.代码实现
import java.io.*;
public class _5_多重背包_dp_2进制优化 {
/*
* 第i个物品最多选s个, 可以表示为 i个物品选1个、2个、4个、8个、16个直到小于s的2的幂次
由此转换成0 1背包问题。
S == 2000, log(2^S) = 11
时间复杂度 1000 * 11 * 2000 = 2 * 10^7
*/
public static void main(String[] args) throws IOException {
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
String ss[] =br.readLine().split(" ");
int N=Integer.parseInt(ss[0]);
int V=Integer.parseInt(ss[1]);
int v[]=new int[22002];
int w[]=new int[22002];
int cnt=0;
for (int i = 1; i <=N ; i++) {
ss=br.readLine().split(" ");
int a=Integer.parseInt(ss[0]);
int b=Integer.parseInt(ss[1]);
int s=Integer.parseInt(ss[2]);
int k=1;
//第i个物品最多选s个, 可以表示为 i个物品选1个、2个、4个、8个、16个直到小于s的2的幂次
while(k<=s){
cnt++; //转换后的数量
v[cnt]=a*k; //总体积
w[cnt]=b*k; //总价值
s-=k; //减去已经转换的k个
k*=2; //每次*2
}
//若最后剩余一组 s>0 再添加进去
if(s>0){
cnt++;
v[cnt]=a*s;
w[cnt]=b*s;
}
}
N=cnt;
//二进制转化 当成01背包进行处理
int dp[] =new int[N+1];
for (int i = 1; i <=N ; i++) {
for (int j = V; j >=v[i] ; j--) {//逆序遍历
dp[j]=Math.max(dp[j],dp[j-v[i]]+w[i]);
}
}
System.out.println(dp[V]);
}
}
优化输入 不浪费空间
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int m = sc.nextInt();
int[] f = new int[m + 1];
for(int i = 0; i < n; i++){
int v = sc.nextInt();
int w = sc.nextInt();
int s = sc.nextInt();
//0-1背包是特殊的多重背包问题
//二进制优化
//k是每种物品的个数
for(int k = 1; k <= s; k *= 2){
//使用01背包模板处理
for(int j = m; j >= k * v; j--){
f[j] = Math.max(f[j], f[j - k * v] + k * w);
}
//减去上一个物品的个数
s -= k;
}
//剩下的个数不是2的幂
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]);
}
}
4.总结
多重背包二进制拆分,使用一维0-1背包进行处理