1. 问题
二维费用的背包问题是指:对于每件物品,具有两种不同的费用,选择这件物品必须同时付出这两种费用。对于每种费用都有一个可付出的最大值(背包容量)。问怎样选择物品可以得到最大的价值。设第i件物品所需的两种费用分别为Ci和Di。两种费用可付出的最大值(也即两种背包容量)分别为V 和U。物品的价值为Wi。
比较简单的理解,就是增加一维物品质量。现在不仅要考虑物品占背包的体积,还有考虑物品本身的质量,最后保证背包中物品的价值最大。
2. 基础实现
背包九讲这里没有给出伪代码,留给读者去实现。笔者的实现如下:
01 二维背包
//滚转数组优化成二维
F[0...V][0...U]
//现在最优子结构为u,v情况下的最大价值,在u或者v任何一个等于0时,初始值都为0
F[0][0...U]=0
F[0...V][0]=0
for i=0 to N
for v=V to Ci
for u=U to Di
F[v][u]=max{F[v-Ci][u-Di]+Wi,F[v][u]}
完全二维背包
//滚转数组优化成二维
F[0...V][0...U]
F[0][0...U]=0
F[0...V][0]=0
for i=0 to N
for v=Ci to V
for u=Di to U
F[v][u]=max{F[v-Ci][u-Di]+Wi,F[v][u]}
多重二维背包
F[0...N][0...V][0...U]
//现在初始条件为i,v,u下的最大价值,初始状态要正确定义。
F[0][0...N][0...V]=0
F[0...N][0...V][0]=0
F[0...N][0][0...V]=0
for i=0 to N
for v=0 to V
for u=0 to U
for k=0 to log(Mi)
F[i][v][u]=max{F[i-1][v-2^k*Ci][u-2^k*Di]+2^k*Wi,F[i-1][v][u]}
3. 物品总个数的限制
如果按照笔者的思考,对每个物品的限制可以直接加在多种背包的Mi上。比如:A物品总共10件,限制最多只能拿3件(
),那么此时可以将Mi由原来的10更新为3。
崔从另外的角度思考:最多拿3件成了另外一维的限制,可以使用上文中伪代码解决这个问题。
4. 复整数域上的背包问题
简单的来思考还是二维的本质,背包九讲未给出伪代码。现在笔者针对01二维背包思考如下(复数用
表示):
//复数F[V]分成F[实部][虚部]
F[0...V][0...U]
//现在最优子结构为u,v情况下的最大价值,在u或者v任何一个等于0时,初始值都为0
F[0][1...U]=0
F[1...V][0]=0
for i=0 to N
for v=V to Ci
for u=U to Di
F[v][u]=max{F[v-Ci][u-Di]+Wi,F[v][u]}
5. 小结
最后崔强调了将一些问题增加的限制当成新的一维来解决,此时又将新问题转化成旧问题。回顾下DP问题的两个必要特征最优子结构和无后效性,从状态转移我们确定最优子结构的成立,无后效性从状态转移方程也能确认没有问题,但是for
遍历循序可能破坏无后效性,这里关键的i从0到N,确保了无后效性。
另外DP最近笔者做的题来感觉,只能解决最优问题。比如最长回文子串问题,机器人走方格问题等。DP和贪婪最直观的不同就是DP从逻辑上保证全局最优(通过状态转移方程和最优子结构保证这一点),而贪婪只是局部最优,如Dijkstra算法、Prim算法、Kruskal算法(DSAA学到了还没有开始记录)等。如果使用DP去求最优解的全集,这是不可能的(除非初始条件有多个最优解)。