动态规划解决的问题多数有重叠子问题这个特点
动态规划与分治法最大的区别在于:适合于用动态规划法求解的问题,经分解后得到的子问题往往不是互相独立的(即下一个子阶段的求解是建立在上一个子阶段的解得基础上,进行进一步的求解)
以下例子总结自公开课 计算机科学及编程导论
第一个简单例子(Python 3.x)
def fib(n):
global numCalls
numCalls+=1
print ("fib called with ",n)
if n<=1:
return 1;
else:
return fib1(n-1)+fib1(n-2)
随着运算的进行,会出现很多冗余的计算,一些我们已经知道结果的计算(重叠子问题)
——解决方法:默记法,在第一次计算时就记录一个值,在后续计算中通过查表使用这个值
改进后的代码:
def fastfib(n,memo):
global numCalls
numCalls+=1
print ("fib called with ",n)
if not n in memo:
memo[n] = fastfib(n-1,memo) + fastfib(n-2,memo)
return memo[n]
def fib(n):
memo={0:1,1:1} ## initialize, memo is a dictionary
return fastfib(n,memo)
当n较大时,执行可发现numCalls大大降低
第二个例子:0-1背包问题
最优子结构(optimal substructure),通过子问题的局部最优方案得到全局优化解决方案
基于决策树的实现:
def maxVal(i,v,w,aw):
print (i,aw)
global callNums
callNums+=1
if i==0:
if w[i]<= aw:
return v[i]
else:
return 0
without_i=maxVal(i-1,v,w,aw)
if w[i]>aw:
return without_i
else:
with_i=v[i]+maxVal(i-1,v,w,aw-w[i])
return max(without_i,with_i)
改进后的代码:
def fastmaxVal(i,v,w,aw,m):
global callNums
callNums+=1
print (i,aw)
try:
return m[(i,aw)]
except KeyError:
if i==0:
ifw[i]<=aw:
m[(i,aw)]=v[i]
return v[i]
else:
m[(i,aw)]=0
return 0
without_i=fastmaxVal(i-1,v,w,aw,m)
if w[i]> aw:
m[(i,aw)]=without_i
return without_i
else:
with_i=v[i]+fastmaxVal(i-1,v,w,aw-w[i],m)
res=max(without_i,with_i)
m[(i,aw)]=res #dictionary memo
return res
总结:动态规划->大大减少调用次数,本质:空间换时间