背包问题

0-1背包

基本思路

这是最基础的背包问题,特色是:每种物品仅有一件,能够选择放或不放。算法

用子问题定义状态:即f[i][v]表示前i件物品恰放入一个容量为v的背包能够得到的最大价值。则其状态转移方程即是:编程

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

这个方程很是重要,基本上全部跟背包相关的问题的方程都是由它衍生出来的。因此有必要将它详细解释一下:“将前i件物品放入容量为v的背包中”这个子问题,若只考虑第i件物品的策略(放或不放),那么就能够转化为一个只牵扯前i-1件物品的问题。若是不放第i件物品,那么问题就转化为“前i-1件物品放入容量为v的背包中”,价值为f[i-1][v];若是放第i件物品,那么问题就转化为“前i-1件物品放入剩下的容量为v-c[i]的背包中”,此时能得到的最大价值就是f[i-1][v-c[i]]再加上经过放入第i件物品得到的价值w[i]。队列

彻底背包

基本思路

这个问题很是相似于01背包问题,所不一样的是每种物品有无限件。也就是从每种物品的角度考虑,与它相关的策略已并不是取或不取两种,而是有取0件、取1件、取2件……等不少种。若是仍然按照解01背包时的思路,令f[i][v]表示前i种物品恰放入一个容量为v的背包的最大权值。仍然能够按照每种物品不一样的策略写出状态转移方程,像这样:ip

f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]|0<=k*c[i]<=v}class

这跟01背包问题同样有O(VN)个状态须要求解,但求解每一个状态的时间已经不是常数了,求解状态f[i][v]的时间是O(v/c[i]),总的复杂度能够认为是O(V*Σ(V/c[i])),是比较大的。基础

将01背包问题的基本思路加以改进,获得了这样一个清晰的方法。这说明01背包问题的方程的确是很重要,能够推及其它类型的背包问题。但咱们仍是试图改进这个复杂度。循环

多重背包

基本算法

这题目和彻底背包问题很相似。基本的方程只需将彻底背包问题的方程略微一改便可,由于对于第i种物品有n[i]+1种策略:取0件,取1件……取n[i]件。令f[i][v]表示前i种物品恰放入一个容量为v的背包的最大权值,则有状态转移方程:方法

f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]|0<=k<=n[i]}im

复杂度是O(V*Σn[i])。

混合三种背包

01背包与彻底背包的混合

考虑到在P01和P02中给出的伪代码只有一处不一样,故若是只有两类物品:一类物品只能取一次,另外一类物品能够取无限次,那么只需在对每一个物品应用转移方程时,根据物品的类别选用顺序或逆序的循环便可,复杂度是O(VN)。伪代码以下:

for i=1..N
    if 第i件物品属于01背包
        for v=V..0
            f[v]=max{f[v],f[v-c[i]]+w[i]};
    else if 第i件物品属于彻底背包
        for v=0..V
            f[v]=max{f[v],f[v-c[i]]+w[i]};

再加上多重背包

若是再加上有的物品最多能够取有限次,那么原则上也能够给出O(VN)的解法:遇到多重背包类型的物品用单调队列解便可。但若是不考虑超过NOIP范围的算法的话,用P03中将每一个这类物品分红O(log n[i])个01背包的物品的方法也已经很优了。

固然,更清晰的写法是调用咱们前面给出的三个相关过程。

for i=1..N
    if 第i件物品属于01背包
        ZeroOnePack(c[i],w[i])
    else if 第i件物品属于彻底背包
        CompletePack(c[i],w[i])
    else if 第i件物品属于多重背包
        MultiplePack(c[i],w[i],n[i])

在最初写出这三个过程的时候,可能彻底没有想到它们会在这里混合应用。我想这体现了编程中抽象的威力。若是你一直就是以这种“抽象出过程”的方式写每一类背包问题的,也很是清楚它们的实现中细微的不一样,那么在遇到混合三种背包问题的题目时,必定能很快想到上面简洁的解法,对吗?

相关文章
相关标签/搜索