01揹包 Hdu Bone Collector(2602)d

我在学习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       

        00)     

        0       

       0

       0

       0

       0

       0

       0

       0

       0

       0

       0

       15

       0

       0

       0

       0

       0

       1

       1

       1

       1

       1

       1

       24)  

       0 

       0      

       0

       0

       2

       2

       2

       2

       2

       3

       3

       33

       0

       0

       0

       3

       3

       3

       3

       5

       5

       5

       5

       42

       0

      0

       4

       4

       4

       7

       7

       7

       7

       9

       9

       51

       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容量的揹包根据上一次存的内容(上一次是以前所有物品加上后的最大价值),求出这一次加上该物品最大的价值。

     大家如果还不理解的话多看几遍就可以理解了,试着把表填一填。

如果有错还希望大家指出,谢谢!!

点赞