背包问题描述
有n个物品,它们有各自的体积和价值,现有给定容量的背包,如何让背包里装入的物品具有最大的价值总和?
类似的问题非常多:
比如每个任务都有时间和价值,我们有一定的时间,现在在有限的的时间里完成最大价值的任务,如何安排?
任务 | A | B | C | D | E | F | G |
---|---|---|---|---|---|---|---|
所得收益 | 7 | 9 | 5 | 12 | 14 | 6 | 12 |
需要时间 | 3 | 4 | 2 | 6 | 7 | 3 | 5 |
下面就以这个问题来进行分析。
分析
如果简单的用 收益/时间来表示收益率,其实非常容易解决这个问题,然而任务是无法分解的。不能做三分之一就停止。
我们还是用背包问题求解。
背包问题的核心是定义目标和找到状态转移公式。
求解
定义目标:求最大收益。
v[i][j] 来表示最大收益,背景:j是背包容量(时间),前i个物品的组合;
这个是关键定义。
然后我们找到相邻两步的关系(注意我们做任何事情都需要极度关注相邻状态,比如马尔科夫链)
v[i][j] 和上一个i-1个物品组合有何关系?
i物品太大,j放不下,那么 v[i][j]=v[i-1][j]
i物品可以放,那么 max(v[i-1][j], v[i-1][j-s[i]] + v[i])就是最优的。
这一步是第二个关键,我们分解一下。
- 放得下,那么放的话,则 v[i-1][j-s[i]]+v[i] 这就是放完的最优状态
- 放得下,但是不放, 收益其实不变,还是上一个状态的v[i-1][j]
那么再反过来思考一下:
v[i][j]是最优的
v[i-1][j-si]是最优的,空间变小后,收益增加v[i], 那么必须比较一下,到底是放进去带来的总体更好,还是不放留给后面的更好?
方法一: 递归
def value(n, space):
'''
knapsack递归解法
选择第n个item的时候,到底是选择做还是不做;选择不做,空间不变,收益不变;
如果选择做,那么space要减掉,收益增加v(n)
:param n:
:param space:
:return:
'''
if n==0:
return 0
if item[n][1] >space:
return value(n-1,space)
return max(value(n-1,space),
value(n-1,space-item[n][1])+item[n][0])
方法二:递归记忆
方法三:直接数组
数组的方法也放在这里,总体代码如下,请忽略格式。
import