背包九讲感悟

0基本背包问题---n个物品,每种只有一个可以用

1,弄清楚最优解与所给的哪几个参数有关;-----注意这里的最优解指的是子问题的最终解,将泛化的i当做确定的值

2,最优解是到第i个决策时候的最优解。而不是第i个决策已经有了之后的最优解。比如01背包问题,就是第i个决策时的最优解,那么第i个决策包含第i个物品是否放进去,从而以此和第i-1个最优解联系

---当问题是“恰好填满”时,这个时候f[i][v]表示的是:第i个刚好将v填满时的最优解

3,写程序时V从大到小迭代还是从小到大,取决于你是用先求子问题,还是先求父问题。

(当用一维数组解决背包问题时,为了保证等是右边是第i-1行的v,就要保证更新从后往前更新,没有被覆盖)

4, for v=V..cost

        f[v]=max{f[v],f[v-cost]+weight}

这样写,是因为当外面有i循环时,我们只要求到cost时,其余就不要求得了,并且f[v-cost]=初始值

5,背包问题初始化:

不同要求不同的初始化值。。。

为什么呢?可以这样理解:初始化的f数组事实上就是在没有任何物品可以放入背包时的合法状态。如果要求背包恰好装满,那么此时只有容量为0的背包可能被价值为0nothing“恰好装满其它容量的背包均没有合法的解,属于未定义的状态,它们的值就都应该是-∞。如果背包并非必须被装满,那么任何容量的背包都有一个合法解什么都不装,这个解的价值为0,所以初始时状态的值也就全部为0了。


----首先保证的是最优解,然后才是装满。。那么如何看最优解条件下是否转满呢?----用是否初始值看

1完全背包问题---每种物品有无数件可以用


方法1,先写出状态转移方程,f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]|0<=k*c[i]<=v}----时间复杂度:O(N*V)个f,每个f又执行v/c[i]次,然后通过对数据进行处理:1】价值大,空间小或者等于其他的物品留下来,其余物品删去2】对于第i种物品,只保留v/c[i]个就行了---二进制改进:【将第i种物品分成若干件物品,其中每件物品有一个系数,这件物品的费用和价值均是原来的费用和价值乘以这个系数。使这些系数分别为1,2,4,...,2^(k-1),n[i]-2^k+1,且k是满足n[i]-2^k+1>0的最大整数。例如,如果n[i]为13,就将这种物品分成系数分别为1,2,4,6的四件物品】------>状态方程进化:f[i][v]=max{f[i-1][v],f[i][v-c[i]]+w[i]}----时间复杂度:O(NV)

方法2,和01背包相比,第i次选物品的时候,只要保证i仍在选择范围之类即可:

for i=1..N

    for v=0..V

        f[v]=max{f[v],f[v-cost]+weight}

2多重背包问题---每种背包最大个数一定

f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]|0<=k<=n[i]}----可以用上面那个二进制,将O(V*Σn[i])复杂度改为O(log n[i]*V)

伪代码:

procedure MultiplePack(cost,weight,amount)

    if cost*amount>=V//此时相当于此种有无数个可取

        CompletePack(cost,weight)

        return

    integer k=1

    while k<num

            ZeroOnePack(k*cost,k*weight)

            amount=amount-k

            k=k*2

    ZeroOnePack(amount*cost,amount*weight)

3二维费用的的背包问题---每件物品不仅有空间代价,还有重量代价

状态转移方程:f[i][v][u]=max{f[i-1][v][u],f[i-1][v-a[i]][u-b[i]]+w[i]}

-------->也可以用二维数组完成

所谓二维费用,就是多了个对所有物件有个总体性限制,比如总的物品个数不能超过M

4分组的背包问题---有n组,每组中有多个物品,但只能选一个

状态转移方程:f[k][v]=max{f[k-1][v],f[k-1][v-c[i]]+w[i]|物品i属于第k组}

实现过程:

for 所有的组k

    for v=V..0

        for 所有的i属于组k

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

注意这里的三层循环的顺序,甚至在本文的beta版中我自己都写错了。“for v=V..0”这一层循环必须在“for 所有的i属于组k”之外。这样才能保证每一组内的物品最多只有一个会被添加到背包中。

5有依赖的背包问题---将原本n个物品分为m组,第i组有1个主物件,m[i]个从物件。对于第i组,要么一个不选【1】,要么选了主物件之后再从m[i]个中选0个、1个、2个....【2^m[i]】----共1+2^m[i]中策略

解题思想:当代价一样时,只需要留下价值最大的组合。

解题过程:用01背包将每组中的从物件的不同组合/策略变成代价为0,1,...V-n[i](n[i]表示第i组中主物件的代价)时的最佳组合(因为用背包算法求得表示此时价值最大),从而每组中都可以等效为V-n[i]+1个物件<<1+2^m[i]

6背包问题问法变化

1,最优方案的总数

这里的最优方案是指物品总价值最大的方案。以01背包为例。

结合求最大总价值和方案总数两个问题的思路,最优方案的总数可以这样求:f[i][v]意义同前述,g[i][v]表示这个子问题的最优方案的总数,则在求f[i][v]的同时求g[i][v]的伪代码如下:

for i=1..N

   for v=0..V

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

        g[i][v]=0

        if(f[i][v]==f[i-1][v])

            inc(g[i][v],g[i-1][v]

        if(f[i][v]==f[i-1][v-c[i]]+w[i])

            inc(g[i][v],g[i-1][v-c[i]])

如果你是第一次看到这样的问题,请仔细体会上面的伪代码。

2,第k优解

解法:每种状态用一个k维数组存储其k个优解,f[i][v][1..k]

注意:题目对于“第K优解”的定义,将策略不同但权值相同的两个方案是看作同一个解还是不同的解。如果是前者,则维护有序队列时要保证队列里的数没有重复的。


猜你喜欢

转载自blog.csdn.net/u011776818/article/details/80955477