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的背包可能被价值为0的nothing“恰好装满”,其它容量的背包均没有合法的解,属于未定义的状态,它们的值就都应该是-∞了。如果背包并非必须被装满,那么任何容量的背包都有一个合法解“什么都不装”,这个解的价值为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优解”的定义,将策略不同但权值相同的两个方案是看作同一个解还是不同的解。如果是前者,则维护有序队列时要保证队列里的数没有重复的。