HDOJ2602——01背包问题
HDOJ2602背包——动态规划(DP)
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).
Sample Input
1
5 10
1 2 3 4 5
5 4 3 2 1
Sample Output
14
这道题是动态规划算法的经典问题——01背包问题。题目翻译过来就是给出一个固定容量的背包和n个物品,每个物品有不同的体积和价值。问如何向背包里放物品可以使价值达到最大。
之所以叫01背包,也就是说每个物品只有两种状态:放入背包/不放入背包。用动态规划的思想,应该先将问题分解成小问题,求出其状态转换方程。设f(i,j)为在i个物品,j的容量下的最优解。易得状态转换方程
f[i,j] = Max{ f[i-1,j-Wi]+Pi( j >= Wi ), f[i-1,j] }
当物品数量为i时,这第i个物品是否应该放入背包?如果这个物品本身比背包容量要大,当然不能放入背包了。如果这个物品比背包容量要小,则应该比较放入和不放入哪种价值更高。
同时,要注意边界问题。当i=1时,此时没有物品,最优解必定为0。同理,当j=0时,最优解也为0。
这道题的核心便是这个方程的推导,参考动态规划之01背包问题
实现
写出来的第一个版本因为用malloc定义数组而忘记free而MLE。发现其实并不需要动态分配数组。直接定义一个元素大小为1001的整型数组便完事。第二次提交的时候又TLE了,感觉是因为函数调用次数太多,实在无奈,去网上看了一下别人的思路。
/*我的版本,TLE了*/
#include<stdio.h>
int max(int a,int b)
{
if(a > b) return a;
else return b;
}
int package(int n,int v,int vol[],int val[])
{
if(n==0) return 0;
else if(vol[n-1] > v) return package(n-1,v,vol,val);
else return max(package(n-1,v,vol,val),package(n-1,v-vol[n-1],vol,val)+val[n-1]);
}
int main()
{
int count;
int i,j,n,v,vol[1001],val[1001]; //n为物品数,v为背包容量
scanf("%d",&count);
while(count--)
{
scanf("%d %d",&n,&v);
for(i=0;i<n;i++)
{
scanf("%d",&val[i]); //记录每个物品的价值
}
for(i=0;i<n;i++)
{
scanf("%d",&vol[i]); //记录每个物品的体积
}
int max = 0,p;
for(i=0;i<n+1;i++)
{
for(j=0;j<v+1;j++)
{
p = package(i,j,vol,val);
if(p > max) max = p;
}
}
printf("%d\n",max);
/*数组清零*/
for(i=0;i<n;i++)
{
val[i] = 0;
}
for(i=0;i<n;i++)
{
vol[i] = 0;
}
}
}
/*用循环实现*/
#include<stdio.h>
#include<string.h>
int main()
{
int count; //记录case的次数
int i,j,n,v,vol[1001],val[1001],dp[1001]; //n为物品数,v为背包容量
scanf("%d",&count);
while(count--)
{
/*数组初始化*/
memset(vol,0,sizeof(vol));
memset(val,0,sizeof(val));
memset(dp,0,sizeof(dp));
scanf("%d %d",&n,&v);
for(i=0;i<n;i++)
{
scanf("%d",&val[i]); //记录每个物品的价值
}
for(i=0;i<n;i++)
{
scanf("%d",&vol[i]); //记录每个物品的体积
}
for(i=0;i<n;i++) //当有i个物品时,dp的最优解。注意边界值。
{
for(j=v;j>=vol[i];j--) //遍历所有背包容量大于物品大小的情况
{
if(dp[j] < dp[j-vol[i]] + val[i]) //在有i个物品的情况下,如果第i个物品放入背包的价值高,则放入物品
dp[j] = dp[j-vol[i]] + val[i];
}
}
printf("%d\n",dp[v]); //输出背包容量最大时的情况
}
}