目录
一、动态规划题型
1.计数问题
-有多少种方式走到右下角
-有多少种方法选出k个数使得和为sum
https://leetcode-cn.com/problems/unique-paths/
2.求最大最小值
-从左上角走到右下角路径的最大数字和
-最长上升子序列长度
https://leetcode-cn.com/problems/maximum-product-subarray/
3.求存在性
-取石子游戏,先手是否必胜
-能不能选出k个数使得和为sum
二、动态规划的组成部分☆
1、确定状态
最后一步
化成子问题
2、转移方程
将最后一步转化为子问题的递推式
3、初始条件和边界情况
初始条件: 靠递推式计算不出来的量,需手工定义
边界情况:防止越界
4、计算顺序
保证在计算时后面计算所需的量在前面的计算过程中已经被算了出来(一般是从前往后)
以一道题为例:
(求最值型动态规划)题目:有2元,5元,7元三种面值的硬币,如何使用最少的硬币数目拼出27元?
1、确定状态
最后一步:在满足题目要求的最优解中的最后一枚面值为a的硬币(相当于结束的出口)
子问题:用最少的硬币(去除最后一枚硬币后剩下子问题的最优解)拼出剩下的27-a元
2、转移方程
f(x) = min{f(x-2)+1, f(x-5)+1, f(x-7)+1}
(因为到达最后一步可以分别有2,5,7三种情况)
注意,这里面的f(x)相当于值为x的最优解,在代码中一般用一个一维数组来储存f(x)的值,从前往后重复计算,如果遇到光靠一个一维的数组无法表示所有情况时,当然可以用到二维三维乃至更多的数组来储存更多的状态
3、初始条件和边界情况
f[0] = 0(通过转移方程无法计算,且需定义)
如果不能拼出某个值x时,那么应该将f[x]设为无穷大(在后面min中会筛选掉)
4、计算顺序
从前往后
def dpdp(coins=27): # 拼27块钱
c = [2, 5, 7] # 总共有2,5,7面值的硬币
MAXNUM = 8888 # 无穷大的数
f = [0 for _ in range(coins+1)]
for i in range(coins+1):
if i == 0:
f[0] = 0 # 初始条件
else:
f[i] = MAXNUM
for j in c:
if i-j >= 0:
f[i] = min(f[i-j]+1, f[i])
if f[coins-1] > MAXNUM/2: # 无穷大表示无法拼出
return None
else:
return f[coins] # 拼27块钱需要的最少硬币数
小结
dp问题感觉和递归的思路有点像,都是重复调用前一时间的自身的值,且都需要递推出口和递推主体,但显然dp的性能更优,相比于递归而言少了很多重复的步骤,一般来说空间复杂度也更小。