动态规划的关键就是将问题子问题化,通过解决子问题最终实现关键问题的解决,我们的一切都是围绕如何更好的解决子问题,以及子问题的情况。最终问题就是从最后的结果往前推,但是我们写代码和计算的却是从前面开始算起。从后往前走给我们思路,从前往后走给我们代码。
裴伯纳数列
重叠的子问题,overlap sub-problem,递归的问题,算的分支其实在不断的增加。就是不断的递增,很多重复的运算,这就是递归的问题。重叠子问题太多,需要保存下来,避免重叠子问题,重叠计算。反着倒过来,就可以,将大问题不断的走下去到小问题。
例题1: 时间不能冲突
求最优解:方法选和不选
例题的推导过程:
新增加的任务是否执行,通过最大化来判断,小的话我们就不执行,大的话就执行。但是如果从最右边的话,我们可以判断第8个任务是否执行,如果不执行那么就看第7个,如果执行那么就看第8个任务如何执行,能够由哪个挨着最近。
例题2
选择不相邻的数字,并且和最大。方法同理:选或者不选的问题
OPT表示的是到这里的最佳方案是什么?而这个最佳方案有两种选择,选或者不选。就只有这两种可能性,所以我们只需要再这两种中选择最好的一种即可。然后根据这个原理进行“递归”。
递归但是存在overlap的问题,所以问题会很多,这就是为什么不用递归的原因,上面也有分析就是很多重叠子问题,很多问题算了又算。
arr = [1,2,4,1,7,8,3]
def rec_opt(arr,i):
if i == 0:
return arr[0]
elif i == 1:
return max(arr[0],arr[1])
else:
A = rec_opt(arr,i-2) +arr[i]
B = rec_opt(arr,i-1)
return max(A,B)
b = rec_opt(arr,6)
还记得我们上面那题是如何算,非递归的嘛,我们就是每次都存下最好的对吧。这里也是一样的。
opt = []
def dp_opt(arr):
opt.append(arr[0])
opt.append(max(arr[0],arr[1]))
for i in range(2,len(arr)):
opt.append(max(opt[i-2] + arr[i],opt[i-1]))
return(opt[len(arr)-1])
例题3:
从里面选择一个方案 使得和为S,有则True 没有则False