动态规划
1. 动态规划的定义
一种数学优化的方法,同时也是编程的方法
2.重要属性
- 最优子结构Optimal Substructure
- 状态转移方程 f ( n ) f(n) f(n)
- 重叠子问题Overlapping Sub-problems
例:最长上升子序列求解
from typing import List
def length_subsquence(nums: List[int]) -> int:
if not nums:
return 0
n = len(nums)
# # dp[i]表示以第i个元素结尾的最长递增子序列长度,初始值为1
dp = [1 for _ in range(n)]
# 遍历每一个元素,求以每一个元素为结尾的最长递增子序列长度
for i in range(n):
for j in range(i):
# 遍历i前面的所有元素,如果nums[j] < nums[i],则求一次dp[i] = max(dp[i],dp[j] + 1)
if nums[i] > nums[j]:
dp[i] = max(dp[i], dp[j] + 1)
return max(dp)
if __name__ == '__main__':
li = [10, 9, 2, 5, 4, 7, 103]
res = length_subsquence(li)
print(res)
3.动态规划结题难点
- 应当采用什么样的数据结构来保存什么样的计算结果
- 如何利用保存下来的计算结果推到出状态转移方程
4.动态规划分类
线性规划
- 各个子问题的规模以线性的方式分布
- 子问题的最佳状态或结果可以存储在一维线性的数据结构中。例如:一维数组,哈希表
- 通常我们会用dp[i]表示第i个位置的结果,或者从0开始到第i个位置为止的最佳状态或结果
基本形式 - 当前所求的值仅仅依赖于有限个先前计算好的值,即dp[i]仅仅依赖于有限个dp[j],j<i
- 当前所求的值仅仅依赖于所有先前计算和的值,即dp[i]是各个dp[j]的某种组合,其中j又0遍历到i-1
例如:斐波那契数列
区间规划 - 各个子问题的规模由不同区间来定义
- 子问题的最佳状态或结果存储在二维数组中
- 这类问题的时间复杂度一般为多项式时间,即对于一个大小为n的问题,时间复杂度不会超过n的多项式倍数
约束规划
在普通的线性规划和区间规划里,一般题目有两种需求: - 统计
- 最优解
非决定多项式 - 时间复杂度
程序运行的时间随着问题规模扩大的增加得有多快
非多项式级时间复杂度:指数级复杂度,如O( 2 n 2^n 2n),O( 3 n 3^n 3n),全排列算法,复杂度为O( n ! n! n!)
多项式级时间复杂度
O( 1 1 1),O( n n n),O( n l o g n nlogn nlogn),O( n 2 n^2 n2),O( n 3 n^3 n3)等