题目描述:
有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
提示:
本题考查多重背包的二进制优化方法。
输入样例
4 5
1 2 3
2 4 1
3 4 3
4 5 2
输出样例:
10
思路分析:
本道题目如果采用把多重背包问题转换成01背包问题,因为每个数字可以用二进制的形式来表示,所以把每个物品数量的二进制位上的1给打包,每次往左移一位,因此个数为1、2、4、8、16、32.......如果碰到不是这些数,就把剩下的另外打包即可,例如3就是把1和2先打包,再把剩下的1单独打包。以上提到的打包都是指当前数量的价值和容积。
算法分析:
一个正整数n,可以被分解成1,2,4,…,2^(k-1),n-2^k+1的形式。其中,k是满足n-2^k+1>0的最大整数。
例如,假设给定价值为2,数量为10的物品,依据二进制优化思想可将10分解为1+2+4+3,则原来价值为2,数量为10的物品可等效转化为价值分别为1*2,2*2,4*2,3*2,即价值分别为2,4,8,6,数量均为1的物品。
代码实现:
#include<stdio.h>
int max(int a,int b)
{ if(a>b)return a;
return b;
}
int dp[15000];
int vj[15000];
int wj[15000];
int main()
{ int n,v,vi,wi,si,i,j,k,count=1;
scanf("%d %d",&n,&v);//物品个数和背包容量
for(i=1;i<=n;i++)
{ scanf("%d %d %d",&vi,&wi,&si);//物品的体积、价值和数量
for(k=1;k<=si;k<<=1)
{ vj[count]=k*vi;
wj[count++]=k*wi;
si-=k;
}
if(si>0)
{ vj[count]=si*vi;
wj[count++]=si*wi;
}
}
for(i=1;i<=count;i++)
for(j=v;j>=vj[i];j--)
dp[j]=max(dp[j],dp[j-vj[i]]+wj[i]);
printf("%d",dp[v]);
return 0;
}