动态规划
- 动态规划 Dynamic Programming,核心思想就是将大问题划分成小问题进行解决,从而一步一步的获得最优解的处理算法
- 动态规划跟分治算法思想类似,但动态规划算法会依赖到上一次计算的结果,每次求解是建立在上一次子阶段的结果基础之上进一步处理,而分治算法分解出来问题往往是独立的
- 动态规划一般可以通过填表的方式进行逐步推进得到最优解
0/1背包问题
01背包问题是经典的利用动态规划算法求解的一个经典案例
有n个物品,他们有各自的重量(w)和价值p,有一个给定容量capacity容量的背包,每件物品只能拿1次,如何将让背包装入的物品具有最大价值
假定 有3个商品 背包的容量是4
物品 | 重量w | 价值p |
---|---|---|
手机 | 1 | 1500 |
电脑 | 4 | 3000 |
电视 | 3 | 2000 |
利用动态规划填表的思想得下一下的表格
- 假定 i 为拿第几件商品表示为列 j为背包的容量表示为行
- 为了方便计算统一,添加一个0件物品和0容量背包的边界情况
- 最优解为B(i,j) 例如若B(0,0)表示拿第0件物品 背包容量为0的时候的最优解 我们很容易得到是0
- B(0,1) 当拿0件物品背包容量是0的时候价值也是0因为没有拿任何物品
- B(1,0) 当拿1件物品背包容量为0的时候因为背包容量为0不能装下任何商品则B(1,0)=0
- B(1,1) 当拿第1件物品且背包容量为1的时候,发现手机的重量正好是1满足背包的容量则B(1,1)=1500,这里1500就是第一件物品即手机的价值 同理B(1,2…4)因为只拿了一件物品无论背包容量多大价值都是1500
- B(2,1) 当拿第2件物品的时候,这时发现物品2电脑的重量是4无法装入容量为1的背包,放不到背包,此时只能去B(2-1,1)的结果,即是第二件物品拿不下,即用装下上一件物品的最优解,同理B(2,2)和B(2,3)
- B(2,4) 这里情况发现了遍历,当前背包的容量是4,放下是可以装下物品2的,此时就有两种情况
- 不拿当前商品 即 B(1,4)即只拿了上一件商品
- 拿当前商品 j价值就是B(1,4-w[i])+p[i] 这里的4-w[i]是因为本来他的容量是4,然后因为你拿了当前商品背包的容量就变少了成了4-当前商品的重量w[i],得到的结果就是上一件商品在重量为4-w[i]时的最优解,然后加上因为此时已经拿了当前商品需要加上当前商品的价值p[i]
- 换成当前我们这里的列子就是B(1,4)与B(1,4-4) + 3000进行比较大小,大的即是B(2,4)的最优解
- 在看一个特殊的列子B(3,4)
- 不拿商品的价值 为B(2,4)为 3000
- 拿了商品则是B(2,4-3)+p[4] ==> B(2,1)=1500 + 2000 = 3500
- 此时发现规律,如果发现当前商品背包放不下的情况即 j < w[i]时,此时的B(i,j)只能等于只拿上一件物品的最优解 即B(i-1,j) 记为 if j<w[i] ==> B(i,j)=B(i-1,j)
- 如果发现当前商品可以装下,就分为装和不装两种情况
- 不装最优解跟我们发现商品装不下的结果是一样的 即 B(i,j)=B(i-1,j)
- 装的情况是我们上一件商品在容量减去当前商品重量的最优解加上当前商品的价值 即B(i,j)=B(i-1,j-w[i])+p[i]
- 此时这两种情况的大的那一个为最优解 B(i,j)=max(B(i-1,j),B(i-1,j-w[i])+p[i])
i/j | 0 | 1 | 2 | 3 | 4 |
---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 |
1 | 0 | 1500 | 1500 | 1500 | 1500 |
2 | 0 | 1500 | 1500 | 1500 | 3000 |
3 | 0 | 1500 | 1500 | 3000 | 3500 |
def knapsack_problem_solution1():
"""
背包问题的第一种解法 使用二维表的形式
:return:
"""
prices = [0, 1500, 3000, 2000]
weight = [0, 1, 4, 3]
max_weight = 4
table = [[0 for i in range(max_weight + 1)] for _ in range(len(prices))]
# 动态规划
for i in range(len(weight)):
for j in range(max_weight + 1):
if weight[i] > j:
# 拿不下
table[i][j] = table[i - 1][j]
else:
# 拿得下 比较拿了这个商品和不拿这个商品的最大值
table[i][j] = max(table[i - 1][j], table[i - 1][j - weight[i]] + prices[i])
for row in table:
print(row)
def knapsack_problem_solution2():
"""
01背包问题第二种解法 使用 一维数组 优化空间使用 但需要注意的是 第二层循环需要从右往左
:return:
"""
prices = [0, 1500, 3000, 2000]
weight = [0, 1, 4, 3]
max_weight = 4
table = [0 for i in range(max_weight + 1)]
for i in range(1, len(prices)):
for j in range(max_weight, 0, -1):
if weight[i] > j:
table[j] = table[j]
else:
table[j] = max(table[j], table[j - weight[i]] + prices[i])
print(table)
if __name__ == '__main__':
knapsack_problem_solution2()