DP问题的特征:
- 重复子问题
- 存在最优子集
背包问题属于经典的DP问题,而0/1背包问题是属于最简单的情况。
0/1的意思是每种物品只有一件,要么放入背包中,要么不放
问题定义:
有N个物品,要放入容量为W的背包中,第i件物品重量为w(i),
价值为v(i),问要怎样放才能在不超过背包容量的基础上,获得最大的价值。
算法描述:
需要用到递归的思想,定义A(i, j)为前i个物品中在容量为j的情况下所能
达到的最大价值,则A(0,j) = 0,A(i,0) = 0(i <= N and j <= W).
如果w(i) > j时,说明第i件物品不能放入背包中,价值不变,所以A(i, j) = A(i - 1, j);
如果w(i) < j时,说明第i件物品能入入背包中,那么又有两种情况,选择放入或者是
不放,如果选择不放入,那么价值还是不变,A(i, j) = A(i - 1, j);如果选择放入,
A(i, j) = v(i) + A(i - 1, j - w(i)),到底选择放入还是不放入,需要取这两种选择的最大值。
用数学表达式可以表示为:
代码
版本1:
- def B(w, v, i, j):
- if i == 0:
- if w[i] <= j:
- return v[i]
- else:
- return 0
- without_i = B(w, v, i - 1, j)
- if w[i] > j:
- return without_i
- else:
- with_i = v[i] + B(w, v, i - 1, j - w[i])
- return max(without_i, with_i)
- if __name__ == "__main__":
- w = [2, 3, 4, 5, 6]#list of item's weight
- v = [1, 4, 3, 6, 8]#list of item's value
- i = len(w) - 1 #number of items
- j = 12 #max weight constraints
- print B(w, v, i, j)
版本1的实现按照算法的思路可以解决问题,但是时间复杂度为指数级,当i取大一点
值时,需要运算很久。这也是一些递归算法存在的问题,这个时候,需要用一种叫做
memorization的技术,也就是储存中间值的方法,纪录之前已经算好的子问题的值,不
用重新计算,直接拿来用就好。
版本二:
- m = {}#memo of value of subproblem, m[(i,j)] = maxValue
- def fastB(w, v, i, j):
- global m
- try:
- return m[(i, j)]
- except KeyError:
- if i == 0:
- if w[i] <= j:
- m[(i, j)] = v[i]
- return v[i]
- else:
- m[(i, j)] = 0
- return 0
- without_i = fastB(w, v, i - 1, j)
- if w[i] > j:
- m[(i, j)] = without_i
- return without_i
- else:
- with_i = v[i] + fastB(w, v, i - 1, j - w[i])
- res = max(without_i, with_i)
- m[(i, j)] = res
- return res
- if __name__ == "__main__":
- w = [2, 3, 4, 5, 6]
- v = [1, 4, 3, 6, 8]
- i = len(w) - 1
- j = 12
- print fastB(w, v, i, j)
线性,用空间换时间的方法。
附加问题:
在求出最大值的同时,需要返回具体的物品最优列表
算法描述:
这个问题需要使用到上一个问题中得到的m字典,从最后一个结果往前算,范围为[n-1,1],当m[(i, j)] > m[(i - 1, j)]时,把物品i放入最优列表中,接着j = j - w(i), 继续往前比,比完之后,最后决定第0个物品是否要加入背包,当最优列表中所有物品重量再加上第0个物品的重量不超过容量限制时,把第0个物品添加到最优列表中,否则不加入
代码:
- def findOptimalSub(m, w, i, j):
- res = []
- maxWeight = j
- sum = 0
- for index in range(i, 0, -1):
- if m[(index, j)] > m[(index - 1, j)]:
- res.append(index)
- j = j - w[index]
- for item in res:
- sum = sum + w[item]
- if maxWeight - sum >= w[0]:
- res.append(0)
- return res