01背包(入门)

N件物品和一个容量为V的背包。i件物品的费用是c[i],价值是w[i]。f[i][v]表示前i件物品恰放入一个容量为v的背包可以获得的最大价值。

模版:

for i=1 to n

  {

for j=0 to c[i]-1

f[i][j]=f[i-1][j];

for j=v to c[i]

f[i][j]=max{f[i-1][j],f[i-1][j-c[i]]+w[i]}

}

这里的f[i-1][j]表示的是:不取这件物品,那么就是前i-1个物品在容量为j的最大价值,因为容量是不变的,所以还是j。

       f[i-1][j-c[i]]+w[i]表示的是:取这件物品,那么就是上一步的最大价值加上这个重量,因为这一步的容量为j,所以上一步的容量要是j-c[i],那样的话加上这次的重量才是j。

现在我们举一个例子

#416. 【背包】采药

题目描述

宁智贤是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”   如果你是宁智贤,你能完成这个任务吗?

输入格式

输入的第一行有两个整数T(1 <= T <= 1000)和M(1 <= M <= 100),用一个空格隔开,T代表总共能够用来采药的时间,M代表山洞里的草药的数目。接下来的M行每行包括两个在1到100之间(包括1和100)的整数,分别表示采摘某株草药的时间和这株草药的价值。

输出格式

输出包括一行,这一行只包含一个整数,表示在规定的时间内,可以采到的草药的最大总价值。

样例数据

input

70 3

71 100

69 1

1 2

output

3

(PS:之前我打了一个模板,出现了重大重大的失误——我中间少了一个循环,没有复制前面的数据。这是当我打了两千多个字,提交了两遍之后才发现的,太坑了。而且样例数据也太水了。而发现了这个错误之后,可以把第二个j循环换成正序的)

这里先列一下数据:

n=3     有3件物品

v=70    背包的总容量为70

C[1]=71      w[1]=100

C[2]=69      w[2]=1

C[3]=1       w[3]=2

一、i=1时  因为c[1]<v,所以第二个j循环是不存在的,所以

都不变,都为0;f[1][1]到c[1][70]也都为0(复制数据)

(就是第一件物品取与不取的问题,取的话它容量会爆,所以只能不取,值就为0)

二、i=2时  j=70 to 69;f[2][1]到f[2][68]都为0(复制数据)

1.f[2][70]=max(f[1][70],f[1][1]+1)=1

        所以说,当上一层都为0的时候,且这个物品可以取,那么就取,值就是它的价值

(就是第二件物品取与不取的问题,因为第一件物品不取,第一件物品的所有的最大价值都为0,且第二件物品不会爆容量,所以取,最大价值为1)

    2.f[2][69]=max(f[1][69],f[1][0]+1)=1

     (在容量为69的条件下,同上)

三、i=3时   j=70 to 1

          1.f[3][70]=max(f[2][70],f[2][69]+2)=3

            如果容量为70,那么第二第三件物品都要取

(到这里就有点复杂了,我们可以理解为:假设3不取,

那么最大价值为1;假设3取,且2也能取,那么就两个都取,最大价值为3)

          2.f[3][69]=max(f[2][69],f[2][68]+2)=2

     如果容量为69,那么只取第三件物品

    (同样的思路:假设3不取,还是1;假设3取,在容量

为69的条件下,2是不能取的,因为容量会爆,这时

最大价值为2)

   3.f[3][68]=max(f[2][68],f[2][67]+2)=2

     (同上,之后都是这样)

……

现在来看一下这个循环的含义

前1个物品在容量为v的最大价值;

前1个物品在容量为v-1的最大价值;

……

前1个物品在容量为c[1]的最大价值;

前2个物品在容量为v的最大价值;

……

前2个物品在容量为c[2]的最大价值

……

依此类推

(且中间一定要复制数据)

大循环i是枚举从第一个物品到第n个物品,小循环j的第二个循环是每一层循环中算的是前i件物品分别对应每个容量的最大价值。而还有一个循环是用来复制数据的。

(一个主循环i=1..N,每次算出来二维数组f[i][0..V]的所有值。f[i][v]是由f[i-1][v]和f[i-1][v-c[i]]两个子问题递推而来。)

从宏观上看:

j循环因为是倒序的,所以当前面比后面小的时候,说明当前这个物品是绝对不能取的,因为光取它就会爆容量。

当前面比后面大的时候,说明这个物品现在分两种情况:取与不取。所以我们分两步:假设这个物品不取,那么最大价值就是上一步所求出来的;假设它取了,你要判断一下前面的物品还能不能取,如果f[i-1][j-c[i]]为0,就说明它什么物品都没有去取;那么自然它的容量是不会爆的,那么就只取这个物品,那么最大价值就是当前物品的价值;如果值不为0,就说明在前面物品取得条件下,还可以取当前物品。

然后最后输出的就是前n件物品在容量为v下的最大价值。

其实这个板子还可以这样打:

for i=1 to n

  for j=v to 0

if j>=c[i] f[i][j]=max{f[i-1][j],f[i-1][j-c[i]]+w[i]}

else f[i][j]=f[i-1][j]

 

 

优化一下空间复杂度:

我们思考一下,事实上,这要求在每次主循环中我们以j=V..0

的顺序推f[j],这样才能保证推f[j]时f[j-c[i]]保存的是状态f[i-1][j-c[i]]的值。(这句话是解释了为什么j要倒序:如果将j的循环顺序从上面的逆序改成顺序的话,那么则成了f[i][j]由f[i][j-c[i]]推知,与本题意不符;现在我要的是f[i][j]由f[i-1][j-c[i]]推知。如果是0到v的话,f[i][j]

就是由f[i][j-c[i]]推知,因为f[i][j-c[i]]刚刚被更新过;而如果是v到0的话,f[i][j-c[i]]就还没有更新过,所以用的是上一层所算出来的值所以这样才可以转换为一维

伪代码如下:

for i=1 to N

    for j=V downto c[i]

        f[j]=max{f[j],f[j-c[i]]+w[i]}

现在的f[v-c[i]]就相当于原来的f[i-1][v-c[i]]。其实你可以

这么想,原来的二维,它的max里的是i-1,就是上一层所求出

来的。所以这个i-1可以不用,直接转换为一维。

然后这边j不用循环到0的原因是:j-c[i]要大于0才有意义,否则的话f[j]就是等于f[j]。

啊,我思路终于清晰了一点。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值