DP--背包九讲学习笔记

一、01背包

(1)空间优化——一维数组

以前不明白为什么这里需要用倒序来推导,现在看明白了。

因为在每一次主循环开始时,dp数组存贮着的就是i-1的状态。

而如果是顺序的,那么假如当前更新到了dp[j],此时的dp[j-c[i]]是已经在本轮中更新过了的,相当于是dp[i][j-c[i]]而不是dp[i-1][j-c[i]]。

这张截图也说得很清楚了。


(2)时间小优化——

第二层循环只进行到cost



(3)初始化的细节——

由两种不同的提问方式来区分

如果是“要求恰好装满”,那么则要使dp[0]=0,而dp[1...W]=-inf.

如果是“不要求恰好装满”,那么dp{0...W]=0.


这是我自己的解释:

因为我们只用一个数组,那么dp初始化的值在将来什么时候用都不确定。

如果是“恰好装满”,那么每个dp的含义都应该是是代表已经装了j重量的物品后可以获得的最大价值。

那么当前任一个dp[j](j!=0)值都不可能是零,因为它里面必须装有东西!!!

除非存在价值为零的商品,且所有其他商品的价值都比零小,而这种题目一般不会出现吧。

如果是“非恰好装满”,那么每个dp的含义应该是代表容量大小为j的背包可以获得的最大价值。

而这个时候,dp[j]为零就是合法的。



(4)一个常数优化

前面的伪代码中有for v=V..1,可以将这个循环的下限进行改进。
由于只需要最后f[V]的值,倒推前一个物品,其实只要知道f[V-w[n]]即可。以此类推,对以
第j个背包,其实只需要知道到f[V-(后n-j个物品的重量总和)]即可,即代码中的




二、完全背包:

(1)最最基础的思路:


(2)但是上面这个时间复杂度太高,可以加一个优化,


(3)接下来是一种更为简单有效的办法。

我们惯常是以+1来枚举的。但是对于某当前的剩余容量V,和当前的物品,

我们最多可以拿V/c[i] 个,而这个数我们可以用二进制来表示,也就是说我们

只需要确定重量为1c[i],2c[i] ,3c[i] ,4c[i] ...的商品拿一件还是不拿就可以了

这样我们就回归到了01背包的问题之中。

那么复杂度就是O{N·V·logV/c[i]}



(4)但是上面的始终是三层循环,实际上我们有O{N·V}的算法。


我自己的理解是,对于每一个状态dp[j](不管是当前i还是上一个循环的i-1)我们都可以选择“维持当前状态不拿商品”,也可以“拿商品”。在下一个状态dp[j+c[i]]也是同样操作。这样一来就是实现了从一种物品一个不拿到一个物品拿多个的各种情况的最优化选择。


三、多重背包:

多重背包就是将完全背包每个物品的个数从无限变为了不一定相同的有限个。
总的来说,多重背包思路基本和完全背包一致,也可以使用二进制数优化至O{N·SEGMA(logV/c[i])}
但是如果想要做到O{N·V}的复杂度则需要用到单调队列的优化。
这里贴一个单调队列优化的博客:

这里顺便解释一下,它里面是这么说的

记住!只有形如 dp[i]=max/min (f[k]) + g[i]  (k<i && g[i]是与k无关的变量)才能用到单调队列进行优化。

优化的对象就是f[k]。

说一下我的理解:
不管dp方程式是几维,只要形如 dp[i]=max/min (f[k]) + g[i]  其中f[k]是更内层的循环的变量即可。 



















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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值