我在学习01揹包的时候也遇到过各种的困难,不能很好地理解01揹包里的数组值得变化。
在这我希望以最简单的方式讲给大家,当然写的也比较囉嗦
01揹包是揹包里最简单的,本人刚刚做完揹包的问题,把自己的学习总结讲给大家,这只是本人的理解,希望大牛看出哪里有错误指出。
01揹包的状态转移方程:f[i, j] = max( f[i-1, j-Wi] + Pi , f[i-1, j] )
这个方程是伪代码写的。i表示有第几件物品,j代表你拥有的费用。max()是你自己定义的一个比较函数。
i表示物品。
j表示费用。
wi表示第i个的费用。
pi表示第i个的价值。
f[ i , j ]表示的是你在面对第i个的时,你有j的费用。
f[ i -1,j ]表示的是你在上一物品选取的时候,有j费用的最优选择。
f[ i-1 ,j-wi ]表示的是你在上一个物品时,拥有j -wi费用的时候的最优选择。
用代码实现时的代码是
for(i=1;i<=n;i++) //i表示第几个物品
for(j=0;j<=m;j++)//j表示费用
if(j>=cost[i])//当费用大于或等于当前的花费是,才可以进行赋值,否则无法放入,也就是数组会越界
pet[i][j]=Max(pet[i-1][j],pet[i-1][j-cost[i]]+value[i]);//转移方程,用cost[i]表示第i个所要花费的费用,vale[i]表示第i个物品的费用
else
pet[i][j]=pet[i-1][j];//不大于的时候跟新数组里的信息
现在大家一定充满疑问就是为什么这样就可以完成,我给大家举一个例子,或许大家就可以理解了。
题目选自杭电的2602题http://acm.hdu.edu.cn/showproblem.php?pid=2602
Problem Description 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 2
31).
Sample Input
1 5 10 1 2 3 4 5 5 4 3 2 1
这一题是最基本的01揹包问题。
不明白题意的自行百度翻译
我给大家ac的代码
<pre name="code" class="cpp">#include<stdio.h>
#include<string.h>
int pet[1001][1001];
int value[1001],cost[1001];
int Max(int a,int b)
{
return a>b?a:b;
}
int main()
{
int t,i,j,n,m;
scanf("%d",&t);
while(t--)
{
memset(pet,0,sizeof(pet));
scanf("%d %d",&n,&m);
for(i=1;i<=n;i++)
scanf("%d",&value[i]);
for(i=1;i<=n;i++)
scanf("%d",&cost[i]);
for(i=1;i<=n;i++)
for(j=0;j<=m;j++)
if(j>=cost[i])
pet[i][j]=Max(pet[i-1][j],pet[i-1][j-cost[i]]+value[i]);
else
pet[i][j]=pet[i-1][j];
printf("%d\n",pet[n][m]);
}
return 0;
}
给大家一个数组值的变化表,希望大家可以看清这个表的变化
第一排表示0~10表示揹包的费用
第一个竖排表示0~5表示的是价值,第二个5~1是表示费用
我们要学会的就是如何填这个表
也就是理解算法计算问题的关键点.
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | |
0(0) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
1(5) | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 |
2(4) | 0 | 0 | 0 | 0 | 2 | 2 | 2 | 2 | 2 | 3 | 3 |
3(3) | 0 | 0 | 0 | 3 | 3 | 3 | 3 | 5 | 5 | 5 | 5 |
4(2) | 0 | 0 | 4 | 4 | 4 | 7 | 7 | 7 | 7 | 9 | 9 |
5(1) | 0 | 5 | 5 | 9 | 9 | 9 | 12 | 12 | 12 | 12 | 14 |
for(i=1;i<=n;i++)
for(j=0;j<=m;j++)
if(j>=cost[i])
pet[i][j]=Max(pet[i-1][j],pet[i-1][j-cost[i]]+value[i]);
else
pet[i][j]=pet[i-1][j];
循环i=1开始,第一件物品费用为5,价值1
第一次进入for循环,j=1,费用为5,进入else pet[ 1 ][ 1 ]=pet[ 0 ][ 1 ];
第二次进入 j=2,费用还是为5 ,进入else petp[1][2]=pet[0][1];
。。。。。。
第五次进入 j=5 进入if(j>=cost[i]) pet[ 1 ][ 5 ]=max(pet[ 0 ][ 5 ],pet[ 0 ][5-cost[i]]+value[i]);可以看出来 cost[i]=5,value[i]=1, pet[ 0 ][ 5 ]=0,pet[ 0 ][ 5-cost[ i ] ]+value[ i ] = 1。pet[ 1 ][ 5 ]=1。
第六次进入j=6 进入if(j>=cost[i]) pet[ 1 ][ 6 ]=max(pet[ 0 ][ 6 ],pet[ 0 ][6-cost[i]]+value[i]);可以看出来cost[i]=5,value[i]=1, pet[ 0 ][ 6 ]=0,pet[ 0 ][ 6-cost[ i ] ]+value[ i ] = 1。pet[ 1 ][ 6 ]=1。
。。。。。。
循环i=2,第二件价值为2,费用为4
第一次进入,j=1,费用为4,不够,进入else pet[ 2 ][ 1 ]=pet[ 1 ][ 1 ]。
第二次进入,j=2.。。。。。。。。
。。。。。。
第四次进入,j=4,费用为4,进入if(j>=cost[ i ])pet[ 2 ][ 4 ]=max(pet[ 1 ][ 4 ],pet[ 1 ][4-cost[ i ] ]+value[ i ]),
pet[ 1 ][ 4 ]=0,pet[ 1 ][ 4-cost[ i ] ]+value[ i ]=2; 所以pet[ 2 ][ 4 ]=2;
第五次进入,j=5,费用为4,进入if( j>=cost[ i ] ) pet[ 2 ][ 5 ]=max( pet[ 1 ][ 5 ] ,pet[ 1 ][ 5-cost[ i ] ]+value[ i ] ).
比较值pet[ 1 ][ 5 ]=1,pet[ 1 ][ 1 ]+value[ i ]=2,所以pet[ 2 ][ 5 ]=2。
。。。。。。
每加上一个物品,从0到10容量的揹包根据上一次存的内容(上一次是以前所有物品加上后的最大价值),求出这一次加上该物品最大的价值。
大家如果还不理解的话多看几遍就可以理解了,试着把表填一填。
如果有错还希望大家指出,谢谢!!