题面
Many years ago , in Teddy’s hometown there was a man who was called “Bone Collector”. This man like to collect varies of bones , such as dog’s , cow’s , also he went to the grave …
The bone collector had a big bag with a volume of V ,and along his trip of collecting there are a lot of bones , obviously , different bone has different value and different volume, now given the each bone’s value along his trip , can you calculate out the maximum of the total value the bone collector can get ?
input
The first line contain a integer T , the number of cases.
Followed by T cases , each case three lines , the first line contain two integer N , V, (N <= 1000 , V <= 1000 )representing the number of bones and the volume of his bag. And the second line contain N integers representing the value of each bone. The third line contain N integers representing the volume of each bone.
Output
One integer per line representing the maximum of the total value (this number will be less than 231).
样例
输入 | 输出 |
---|---|
1 5 10 1 2 3 4 5 5 4 3 2 1 | 14 |
题目分析
就是一道01背包的裸题,所以就给大家讲讲01背包。
你有一个背包,背包有一个容积V。
在你面前有N件物品,每件物品有一个价值v[i],和一个体积c[i] (i=1,2,3…N)
求在不超过背包容积的情况下所能得到的价值总和最大为多少?
一开始不懂算法的时候,觉得只要选性价比高的就可以了。但是有时候性价比高的占体积大,远不如多个小的但性价比低的物品。
当初看别人讲01背包的时候有点晕乎,现在到自己讲了,希望能够让读者看懂。
首先我们用一个二维数组
f[i][j] :存储的是在面对第i件物品的时候背包剩余容积为j此时的最大价值总和。
然后使用两层循环遍历
for(i=1;i<=N;i++){
for(j=0;j<=V;j++){
}
}
我们先假设一下面对第一件物品的时候,(i=1),然后j不断地增加,如果第一件物品不超过我们的容积的话,我们就可以再j>=c[i]那个时候把他装进背包。那么此时我们背包的容积也会相应的减少。
我们刚才讲的二位数组存储的是最大价值总和对吧,那么当我们能够装进第一件物品的时候。
此时的最大价值=之前的最大价值 + 当前物品的价值=0+v[1]=v[1];
然后里层j循环结束之后,i=2即我们此时面对第二件物品,当我们的j递增到可以装下第二件物品的时候,我们是否把他装下去取决于装下去之后价值是否会比之前的最大价值大。
此时我们给出一条动态转移方程:f[i][j]=max(f[i][j],f[i][j-c[i]]+v[i]);
f[i][j]我们知道他的意思
f[i][j-c[i]]+v[i]的意思则是我拿下这个物品,那么价值增加,容积相应减少到前面的一个状态这个状态就是之前的最大价值。
而取最大值的意思则是 我之前的最大价值总和加上这个物品的价值,是否比我不拿这个物品,拿了其他物品即把位置让给其他物品的价值大。
好吧,我尽力了。
经过刚才的讲解你可能已经有点感觉或者完全了解了动态转移方程的含义,接下来讲用一维数组的01背包。虽然是一位数组,但仍然要使用2层循环。
f[j] :存储的是背包剩余容积为j此时的最大价值总和。
于是你马上推出一条新的方程:f[j]=max(f[j],f[j-c[i]]+v[i]);
没错,这就是一维数组对应的动态转移方程。但是此时我们的循环需要改变一下
for(i=1;i<=n;i++){
for(j=V;j>=c[i];j--){
f[j]=max(f[j],f[j-c[i]]+v[i]);
}
}
为什么j是倒着递减的,我们想象一下如果j是递增的时候会出现什么问题,当我达到c[i]的时候我会买下它,但当我达到2*c[i]的时候我们依旧会买下它,造成了重复(此时为完全背包),而01背包的问题背景一件物品只能选一次。而倒着递减完美解决了这个问题。
讲的可能不够清晰,大家可以搜索背包九讲来更深入地理解背包问题。
代码
#include <stdio.h>
#include <string.h>
#define max(a,b) ( (a)>(b)?(a):(b) )
int main(){
int T,n,V,v[1005],c[1005],f[1005],i,j,ans;
scanf("%d",&T);
while(T--){
ans=-1;
scanf("%d%d",&n,&V);
memset(f,0,sizeof(f));
for(i=1;i<=n;i++) scanf("%d",&v[i]);
for(i=1;i<=n;i++) scanf("%d",&c[i]);
for(i=1;i<=n;i++){
for(j=V;j>=c[i];j--){
f[j]=max(f[j],f[j-c[i]]+v[i]);
}
}
for(j=0;j<=V;j++){
ans=max(ans,f[j]);
}
printf("%d\n",ans);
}
return 0;
}