动态规划/ Dynamic Programming (华为机试-购物单)
最近报名了华为的机试,刷题的时候遇到了购物单那道题,于是稍微去了解了一下动态规划。所谓动态规划的优点,我的理解就是用空间去换取了时间,来解决了over-lap subproblem。很多recursion(递归)的题目都可以用dynamic programming来进行优化。
最经典的例子就是Fibonacci Sequence 即 斐波那契数列。
如果用递归的方式来计算f(n)= f(n-1) + f(n-2)时,比如f(n-2)这项,在计算f(n)和 f(n-1)都会计算一次。所以会导致复杂度比较高,而如果我们用一个list来保存每一项的值,那么f(n-2)这项就不需要重复计算了。算出来一次,第二次直接拿来用就行,这大概就是我对动态规划的初步理解。
这里推荐Youtube上 黄浩杰的动态规划两讲, 讲的非常清楚。
接下来举几个例子来讲解一下:
第一题为黄浩杰视频中的一道例题,大概是讲用列表a中的数字能否加起来等于b(每个数字用一次或不用)
比如 a = [5,2,3] b = 7, 答案是True,因为 7 = 5+2
同理 a = [5,2,3] b = 9, 答案是False,
特殊的,a = [1,2,3] b = 0,答案是True
#1. 判断a中的数字能否加起来等于b
import numpy as np
a = [1,4,7,3,5]
b = 20
def dp_subset(a,b):
dp = np.zeros((len(a),b+1),dtype=bool) #dp[i,j] i表示此时为用a[0,i]的数 , j为此时目标值
dp[0,:] = False #a[0]只有一个数,除了0和a[0],不能加出其他数, 所以下面会把dp[0,0],dp[0,a[0]]改为True
dp[:,0] = True #当目标值都为0时,任何长度的a都能得到目标值,即每个数都不使用
dp[0,a[0]] = True
for i in range(1,len(a)):
for j in range(1,b+1):
if a[i] > j:
dp[i,j] = dp[i-1,j] #如果a[i]>j,即这个数大于目标值,则考虑不使用a[i],dp[i,j]和dp[i-1,j]的值是一样的
else:
A = dp[i-1,j] #不用a[i],能否加出来j,则考虑dp[i-1,j]
B = dp[i-1,j-a[i]] #用了a[i],则目标值变成了j-a[i],则考虑dp[i-1,j-a[i]]
dp[i,j] = A or B #两种选择,有一种是True就行
m,n = dp.shape
return dp[m-1,n-1]
print(dp_subset(a,b