9动态规划
1.背包问题
音响 | 3000元 | 4斤 |
笔记本电脑 | 2000元 | 3斤 |
吉他 | 1500元 | 1斤 |
组合1 | 组合2 | 组合3 | 组合4 |
无 | 吉他 | 音响 | 笔记本电脑 |
0 | 1500 | 3000 | 2000 |
组合5 | 组合6 | 组合7 | 组合8 |
吉他和音响 | 吉他和笔记本电脑 | 音响和笔记本电脑 | 吉他、音响和笔记本电脑 |
装不下 | 3500 | 装不下 | 装不下 |
2. 动态规划
使用动态规划可以得到最优解。
背包的承重为1斤 | 背包的承重为2斤 | 背包的承重为3斤 | 背包的承重为4斤 | |
吉他1 1500 | 可以放入背包 G 1500 | 可以放入背包 G 1500 | 可以放入背包 G 1500 | 可以放入背包 G 1500 |
音响4 3000 | G 1500 | G 1500 | G 1500 | S 3000 |
笔记本电脑3 2000 | G 1500 | G 1500 | C 2000 | 3000 vs (2000+1500) -> 3500 |
2.1 实现
bag={
"computer":{"weight":3,"value":2000},
"guitar":{"weight":1,"value":1500},
"sound":{"weight":4,"value":3000}
}
list=[]
goods = []
row=0
def addList(list1,list2):
for i in list1:
list2.append(i)
for key in bag.keys():
sublist=[]
subGood=[]
keyWeight = bag.get(key)["weight"]
keyValue = bag.get(key)["value"]
for column in range(4):
sub=[]
#单元格 = 列的索引+1
weight = column + 1
#判断行,行数=0,直接对比;行数大于0,与上一行进行对比
if(row > 0):
if(weight < keyWeight):
sublist.append(list[row-1][column])
addList(goods[row - 1][column], sub)
elif(weight == bag.get(key)["weight"]):
if( list[row-1][column] < keyValue ):
sublist.append(keyValue)
sub.append(key)
else:
sublist.append(list[row-1][column])
addList(goods[row-1][column],sub)
else:
#如果单元格重量>商品,就判断商品的权重和同位置大小
#判断weight-bag.get(key)["weight"]
if (list[row-1][column] < ( keyValue + list[row-1][weight-keyWeight-1] ) ):
sublist.append( keyValue + list[row-1][weight-keyWeight-1])
sub.append(key)
addList(goods[row - 1][weight-keyWeight-1], sub)
else:
sublist.append(list[row-1][column])
addList(goods[row-1][column],sub)
else:
#直接判断,单元格 < 物品重量
if(weight < keyWeight):
sublist.append(0)
# sub.append([])
else:
sublist.append(keyValue)
sub.append(key)
subGood.append(sub)
list.append(sublist)
goods.append(subGood)
row+=1
#最大值肯定在最后一个值
print("4斤背包容纳的最大价值组合:%s %s" % (list[-1][-1],goods[-1][-1])) #4斤背包容纳的最大价值组合:3500 ['guitar', 'computer']
bag={
"computer":{"weight":3,"value":2000},
"guitar":{"weight":1,"value":1500},
"sound":{"weight":4,"value":3000}
}
list=[]
goods = []
row=0
def addList(list1,list2):
for i in list1:
list2.append(i)
for key in bag.keys():
sublist=[]
subGood=[]
keyWeight = bag.get(key)["weight"]
keyValue = bag.get(key)["value"]
for column in range(4):
sub=[]
#单元格 = 列的索引+1
weight = column + 1
#判断行,行数=0,直接对比;行数大于0,与上一行进行对比
if(row > 0):
if (weight == bag.get(key)["weight"] and list[row - 1][column] < keyValue):
sublist.append(keyValue)
sub.append(key)
elif(weight > bag.get(key)["weight"] and list[row-1][column] < ( keyValue + list[row-1][weight-keyWeight-1] )):
sublist.append(keyValue + list[row - 1][weight - keyWeight - 1])
sub.append(key)
addList(goods[row - 1][weight - keyWeight - 1], sub)
else:
sublist.append(list[row-1][column])
addList(goods[row - 1][column], sub)
else:
#直接判断,单元格 < 物品重量
if(weight < keyWeight):
sublist.append(0)
else:
sublist.append(keyValue)
sub.append(key)
subGood.append(sub)
list.append(sublist)
goods.append(subGood)
row+=1
#最大值肯定在最后一个值
print("4斤背包容纳的最大价值组合:%s %s" % (list[-1][-1],goods[-1][-1]))
3.背包问题FAQ
3.1 增加一个商品时,需要重新执行前面所做的计算吗?
不需要,动态规划逐步计算到最大价值
背包的承重为1斤 | 背包的承重为2斤 | 背包的承重为3斤 | 背包的承重为4斤 | |
吉他1 1500 | 可以放入背包 G 1500 | 可以放入背包 G 1500 | 可以放入背包 G 1500 | 可以放入背包 G 1500 |
音响4 3000 | G 1500 | G 1500 | G 1500 | S 3000 |
笔记本电脑3 2000 | G 1500 | G 1500 | C 2000 | 3000 vs (2000+1500) -> 3500 |
iphone1 2000 | I 2000 | I 2000 | I G 3500 | 3500 vs (2000+2000) -> 4000 |
3.2 沿着一列往下走,最大价值有可能降低吗?
不可能。每次迭代时,你都存储当前的最大价值。最大价值不可能比以前低。
3.3 行的排列顺序发生变化时结果将如何
答案没有变化。各行的排列顺序无关紧要
3.4 可以逐列而不是逐行填充网格吗
可能有影响
3.5 增加一件更小的商品,将如何呢?
还可以偷一条项链,重0.5斤,价值1000。需要调整网格,将粒度设置为0.5。
3.6 可以偷商品的一部分吗?
没法处理。使用动态规划时,要么考虑拿走整件商品,要么考虑不拿,而没法判断该不该拿走商品的一部分。
但使用贪婪算法可轻松的处理这种情况。首先,尽可能的多拿价值最高的商品;然后拿价值次高的商品,以此类推。
3.7 旅游行程最优化
去伦敦度假,假期2天,没有办法前往每个地方游览。
0.5 | 1 | 1.5 | 2 | |
威斯敏斯特教堂0.5 7 | 7 | 7 | 7 | 7 |
环球剧场0.5 6 | 7 | 13 | 13 | 13 |
英国国家美术馆1 9 | 7 | 13 | 16 | 22 |
大英博物馆2 9 | 7 | 13 | 16 | 22 |
圣保罗大教堂0.5 8 | 8 | 15 | 17 | 8+16->24 |
3.8 处理相互依赖的情况
假设还想去巴黎,因此添加了几项。从伦敦前往巴黎需要0.5天。
如果单独去巴黎的某个景点,每个景点用时1.5天。
到达巴黎后,每个景点用时1天,而不是1.5天。
埃菲尔铁塔 | 1.5天 | 8 |
卢浮宫 | 1.5天 | 9 |
巴黎圣母院 | 1.5天 | 7 |
3.9 计算最终的答案时会设计两个以上的子背包吗
根据动态算法的设计,最多只需合并两个子背包,即根本不会设计两个以上的子背包。不过这些子背包可能又包含子背包。即cell[i-1][j-当前商品的重量]可能包含子背包。
3.10 最优解可能导致背包没装满吗?
完全可能
3.11 启示
- 动态规划可帮助你在给定约束条件下找到最优解。
- 在问题可分解为彼此独立且离散的子问题时,就可使用动态规划来解决。
- 每种动态规划解决方案都涉及网格。
- 单元格中的值通常就是你要优化的值。背包问题中,单元格的值为商品的价值
- 每个单元格都是一个子问题,因此应考虑将问题分为子问题,这有助于找出网格的坐标轴。
4 最长公共子串与最长公共子序列
最长公共子串是两个字符串中最长的相同字符串。
最长公共子序列是两个字符串中相同的字符数,不需相连,类似动态规划。
5.动态规划应用
1.生物学家根据最长公共序列来确定DNA链的相似性,进而判断两种动物或疾病有多相似。
2.使用动态规划来比较2个文件的不同,如diff,git