注意: 切忌望文生义,用动态规划的名字反推算法!实际上,动态规划更像是高中数列题的升级版。
能用动态规划解决的问题需要满足的两点
- 大问题拆分成小问题
- 小问题被重复调用
应用动态规划——将动态规划拆分成三个子目标
1. 建立状态转移方程:
一个思维:当知 f(1)~f(n-1)的值,然后想办法利用它们求得f(n)
2. 缓存并复用以往结果
如果没有合适的处理,时间复杂度很有可能会是指数型的
3. 按顺序从小往大算
例子:
一. 斐波那契数列(简单)
斐波那契数列:0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233……
它遵循这样的规律:当前值为前两个值的和。那么第n个值为多少?
首先,我们可以很容易得到状态转移方程: f ( n ) = f ( n − 1 ) + f ( n − 2 ) , n > = 2 f(n) = f(n-1) + f(n-2), n >= 2 f(n)=f(n−1)+f(n−2),n>=2
1. 简单递归(反例):
def fib(n):
if n < 2:
return n
else:
return fib(n-1) + fib(n-2)
print(fib(100))
时间复杂度指数级别,和没有写效果一样
2. 动态规划
def fib(n):
result = list(range(n+1)) # 缓存以往的结果,方便复用(目标2)
for i in range(n+1): # 按顺序从小往大算(目标3)
if i < 2:
result[i] = i
else:
# 使用状态转移方程(目标1),同时复用以往结果(目标2)
result[i] = result[i-1] + result[i-2]
return result[-1]
if __name__ == "__main__":
result = fib(100)
print(result)
二、不同路径(困难)
class Solution():
def uniquePaths(self, m, n): # 将二维列表初始化为1,以便之后用于缓存(目标2)
dp = [[1]*n]*m
for i in range(1, m): # 外循环逐行计算(目标3)
for j in range(1, n): # 内循环逐行计算(目标3)
dp[i][j] = dp[i][j-1] + dp[i-1][j] # 状态方程(目标1),以及中间结果复用(目标2)
return dp[m-1][n-1]
s = Solution()
m = 3
n = 2
print(s.uniquePaths(m, n))