苹果-01背包(专为新手打造)(包括一维数组和二维数组不同的变型)

直接就上题了。

                               苹果

 

题目描述

ctest有n个苹果,要将它放入容量为v的背包。给出第i个苹果的大小和价钱,求出能放入背包的苹果的总价钱最大值。

输入

有多组测试数据,每组测试数据第一行为2个正整数,分别代表苹果的个数n和背包的容量v,n、v同时为0时结束测试,此时不输出。接下来的n行,每行2个正整数,用空格隔开,分别代表苹果的大小c和价钱w。所有输入数字的范围大于等于0,小于等于1000。

输出

对每组测试数据输出一个整数,代表能放入背包的苹果的总价值。

样例输入

3 3
1 1
2 1
3 1
0 0

样例输出

2

来源

NYOJ

 这题苹果,是01背包里面最基础的一道题,也就是动态规划的一个狠典型的例子。对,是狠。本人愚钝,写这篇博客的时候也是一个新手,也是属于刚刚理解并且会码01苹果背包的。作为一个新手,我感觉我应该是可以理解新手所不理解和很迷惑的地方的。所以趁着我自己刚刚弄懂,有些傻傻的问题还没有忘记,我赶忙写下了这篇博客。虽然有些问题是傻傻的,但是就是这些傻傻的问题让我停了好长时间。

 

和我一样的想要搞懂这题的萌新们肯定都在网上搜或者在书上还是什么地方听到过01背包的状态转换方程

f[i][j]=max(f[i-1][j-w[i]]+p[i],f[i-1][j]);先看一下这个方程,没熟悉过的先熟悉一下,熟悉的也再瞅一瞅看一看。下面会逐步看到这个方程的部分。

看过题目并思考的你应该知道这道题的意思,就是几个苹果,每个苹果有不同的价值和体积(但并不是体积越大的苹果越值钱哦),要用一定容量的背包装,并获得最大的价值。

先不管别的,一步一步来,让我们先看一个例子(由于这题目的样例实在是太特殊了,后续理解会不太好理解,所以我随便换组样例,其实也不是随便想的)

样例输出

5 10
4 6
5 4
6 5
2 3
2 6						

 

看着这个例子,让我们来模拟一下环境,地上呢,有五个苹果,有贵的当然也有不是太贵的,又大的当然也有比较小的,你呢有一个包包,体积为10的大包包,看到地上有苹果,你当然要捡起来啦,有包包用不能浪费啊,贪心的你又想又有限的包包装到最大的财富,于是你就开始思考,怎么才能做到这一点,如果以大小为10的的包来装所有的5个苹果,可以想到好多好多中装法,还要从这么多种装法中取最赚的装法,虽然你的脑子是很聪明但是无法比较方便的又相对快捷的想出答案,刚好你有了解动态规划,就开始规划,怎么规划呢,那就先假设地上你只看到了一个苹果,假设手头的不是一个大小为10的包,而是有11个不同大小的包,大小是从0--10,这时候可以装进去这一个苹果的背包的价值v[1][j]原本是0,在装了一个苹果之后总价值va变成了va[i-1][j-w[i]]+p[i](顺道小小的解释一下这个部分的式子,i表示的是i个苹果,此时的i就是1,就是前一个苹果,va[i-1][j-[w[i]]表示的是上一个状态的最高价值,在这里就是前0个苹果的最大价值,j表示的就是不同大小的背包,w[i]就指的是该i个苹果的价值),这时候你要进行两个比较:

第一个比较就是看到的这个苹果能不能装,是比较背包的大小和地上的苹果的大小,很显然,必须要做到背包的大小>=苹果的大小;

第二个比较就是看到的这个苹果要不要装,于是就要比较一下装的价值大还是不装的价值大,想到这儿,可能就会突兀的冒出一个傻傻的问题:需要比较吗?装了怎么会比不装的价值小?

冒出这个问题的朋友们可能就是把这个式子va[i][j]里的i的概念弄混淆了更有可能是是把va[i][j]这个式子的概念忘记了,在回想一下,va[i][j]的意思并不是前i个苹果的总价值,而是在背包大小j的限制下前i个苹果所能装下的最大价值,并不是把前i个苹果全部装了进去,这是做不到的,并且值得注意的是,这个i并不是第i个苹果而是前i个苹果,更形象具体的解释就是,va[i-1][j]和va[i][j]相差的并不是一个苹果。

解决了这个问题,再回到原来的例子中,由于是第一次装苹果,这样的比较也很简单,肯定是要装的,就有了这样的表

iwp\j 12345678910
1460006666666
254          
365          
423          
526          

假设有了下一个苹果,每个包都有不同的情况,有拾了上个苹果的当然也有拾不起的,重复上面的两个比较并做出选择,于是就有了下一张表格

iwp\j 12345678910
1460006666666
254000666661010
365          
423          
526          

在这个情况里,我在举一个例子,以便更好的理解,我们来看一下背包大小为5的例子,就是va[2][5],根据上面的式子,va[i][j]=va[2][5]=max(va[i-1][j-w[i]]+p[i],va[i-1][j])

                                      =max(va[2-1][5-5]+4,va[2-1][5])=max(va[1][0]+4,va[1][5])

比较一下va[1][0]+4和va[1][5]的值选最大的,这时会发现,后者大,也就是说不应该装这个苹果。

就这个重复工作,知道表格变成下面的样子

iwp\j 12345678910
1460006666666
254000666661010
365000666661011
423033669991011
526066991212151515


就很成功的找到了最大的价值是15.(上面的过程没有听懂的朋友在下方评论,会尽我最大努力解释的哦)

理论弄完了,上代码先

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int va[1010][1010];
int main()
{
    int n,v,w,p;
    while(scanf("%d%d",&n,&v),n||v)
    {
        memset(va,0,sizeof(va));
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d",&w,&p);//简化处理,这样做就可以少定义两个数组,偷个懒呗。
            for(int j=0;j<=v;j++)
        {
            va[i][j]=va[i-1][j];
            if(j>=w)
            va[i][j]=max(va[i-1][j],va[i-1][j-w]+p);//由于偷了个懒,w[i]和p[i]就相当于w、p。
        }
        }
       printf("%d\n",va[n][v]);
    }
    return 0;
}

可能到这里又会出现问题了,i为什么从1开始?是为了避免下标越界,但我在学习这个01背包的时候,查百度看到过i从0开始的版本,目前,还不是太懂,欢迎各位大佬来指点指点我。

这是二维的,下面有一个一维的代码。

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int va[1010];
int main()
{
    int n,v,w,p;
    while(scanf("%d%d",&n,&v),n||v)
    {
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&w,&p);
        for(int j=v;j>=w;j--)
            va[j]=max(p+va[j-w],va[j]);//这里是一维和二维的其中之一的区别一
    }
    printf("%d\n",va[v]);
    memset(va,0,sizeof(va));//这里也是一维的和二维的其中之一的区别二
    }
    return 0;
}

区别一在二维里内循环是正序的,而在一维中内循环式逆序的,这是因为一维中背包的状态需要上一个来继承,从最大的开始方便下一次va[j]继承上一状态的。而在二维背包中不存在,因为在二维背包中没有需要继承的关系。

区别二代码里提到的小小的区别就是一维的必须在每一次数据判断好之后接上一个清空数组的操作,不然某些上一次数据会被继承下来。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值