背包问题总结

来源:互联网 发布:java求素数 编辑:程序博客网 时间:2024/04/28 19:41

一:01背包
题目描述:N件物品放入容量为V的包裹中,第i件物品体积Ci,价值Wi。怎么装可以使价值和最大。
方法:时间复杂度O(VN),空间复杂度O(V)
定义F[v]为前i件物品放入容量v的背包中得到的最大价值。通过优化F[i,v]中仅保留F[v]即可。
代码

memset(0,F,sizeof(F));for(int i=1;i<=N;i++){    for(int v=V;v>=C[i];v--)        F[v]=max(F[v],F[v-C[i]]+W[i]);}

如果要求恰好装满背包,初始化时除了 F[0] 为 0,其它F[1..V ] 均设为 −∞。

二:完全背包
题目描述:N件物品放入容量为V的包裹中,每种物品都有无限件,每件占容量Ci,价值Wi,如何使总价值最大。
方法:复杂度O(VN),与01相同。代码仅与01背包v循环次序不同。

memset(0,F,sizeof(F));for(int i=1;i<=N;i++){    for(int v=C[i];v<=V;v++)        F[v]=max(F[v],F[v-C[i]]+W[i]);

可行原因:为什么这个算法就可行呢?首先想想为什么 01 背包中要按照 v 递减的次序来循环。让 v 递减是为了保证第 i 次循环中的状态 F[i,v] 是由状态 F[i − 1,v − Ci] 递推而来。
换句话说,这正是为了保证每件物品只选一次,保证在考虑“选入第 i 件物品”这件策略时,依据的是一个绝无已经选入第 i 件物品的子结果 F[i − 1,v − Ci]。而现在完全背包的特点恰是每种物品可选无限件,所以在考虑“加选一件第 i 种物品”这种策略时,却正需要一个可能已选入第 i 种物品的子结果 F[i,v − Ci],所以就可以并且必须采用 v递增的顺序循环。这就是这个简单的程序为何成立的道理。

三:多重背包
题目描述:有 N 种物品和一个容量为 V 的背包。第 i 种物品最多有 Mi件可用,每件耗费的空间是 Ci,价值是 Wi。求解将哪些物品装入背包可使这些物品的耗费的空间总和不超过背包容量,且价值总和最大。
方法1:把第 i 种物品换成 Mi件 01背包中的物品,则得到了物品数为 ΣMi的 01 背包问题。直接求解之,复杂度仍然是O(V ΣMi)。
方法2:将第 i 种物品分成若干件 01 背包中的物品,其中每件物品有一个系数。这件物品的费用和价值均是原来的费用和价值乘以这个系数。令这些系数分别为1,2,4,8,16…..

    def MultiplePack(F,C,W,M)        if C · M ≥ V            CompletePack(F,C,W)            return        k ← 1        while k < M            ZeroOnePack(kC,kW)            M ←M − k            k ← 2k    ZeroOnePack(C · M,W · M)

四:混合背包
1.01与完全背包混合
根据该件物品是单件还是无限件,调整v的循环方向

for i ← 1 to Nif 第 i 件物品属于 01 背包for v ← V to CiF[v] ← max(F[v],F[v − Ci] + Wi)else if 第 i 件物品属于完全背包for v ← Ci to VF[v] ← max(F[v],F[v − Ci] + Wi)

2.三种混合

for i1 to N    ifi 件物品属于 01 背包        ZeroOnePack(F,Ci,Wi)    else ifi 件物品属于完全背包        CompletePack(F,Ci,Wi)    else ifi 件物品属于多重背包        MultiplePack(F,Ci,Wi,Ni)
0 0
原创粉丝点击