Rate this post
一、什么是动态规划思想:
我们在遇到很多的算法求解问题时,通常会被给出的复杂、繁琐的需求所吓倒,这导致我们失去冷静严密的思考逻辑而变得暴躁不已,最终放弃这一个算法题,但其实,很多时候,我们遇到的问题都是可以对其进行拆分和化简的。当我们学会合理的拆分、化简、提取关键步骤,很多的算法难题就会因人而结,而在诸多算法思路中,最常用,也是最好用的就是动态规划方法。
什么是动态规划呢?动态规划的其实就是用分治的思想来解决冗余,因此,动态规划算法是一种将问题划分为更小的、具有相同步骤的子问题,并在此基础上存储子问题的解,这样就可以做到而避免重复计算的子问题,以解决最优的算法策略。其实一般来讲,只要某一问题可以划分成更小的子问题,并且原始问题中的最优解包含了他的子问题的最优解,那么这时我们就可以考虑用动态规划的方法来解决。那么我们不难看出,动态规划法与分治法和贪心法类似,它们都是将问题分解为更小的、相似的子问题,然后通过对子问题进行操作和实现来给整个问题提供最优解。
二、经典的动态规划算法实例:
在上面我们介绍了动态规划算法的基本思想和操作过程,但是光以文字的方式绝对无法让我们吃透这个思想方法,所以我们不妨看几道经典的动态规划例题。
1、题目描述(据说是网易的算法题)
现在有n个学生站成一列,每个学生有自己的能力值,憨憨想从这 n 个学生中按照一定的顺序选取 k 名学生。现在要求相邻的两个学生的位置编号之差不超过 d,从而使得这 k 个学生的拥有的能力值乘积最大,要求返回这最大的乘积。
从题目中,我们不难看出,按照最基本的思路我们会不停的遍历这一列学生,然后不断累加他们的能力值并判断是否是最大。然后等遍历结束后输出这个值。但是这样的做法显然非常的消耗时间(即时间复杂度太大),那么进一步分析,在此题中,如果先求解在第i个学生的位置下,往后数j(j
代码如下:(所用语言是python3)
n=input()
a=[int(i) for i in raw_input().split()]
k,d=[int(i) for i in raw_input().split()]
# n=36
a_max=a_min=a
#轮询k-1趟即可
for i in range(0,k-1):
_max=[-float(‘inf’)]*n #将最大值的数组赋值无穷小
_min=[float(‘inf’)]*n #将最小值的数组赋值无穷大
for j in range(i+1,n):
if j<=d+i: #下面对应的min、max都是考虑到array[j]为负值的情况下
temp_max = max(max(ii*a[j] for ii in a_max[i:j]),max(ii*a[j] for ii in a_min[i:j]))
temp_min = min(min(ii*a[j] for ii in a_max[i:j]),min(ii*a[j] for ii in a_min[i:j]))
else:
temp_max = max(max(ii*a[j] for ii in a_max[j-d:j]),max(ii*a[j] for ii in a_min[j-d:j]))
temp_min = min(min(ii*a[j] for ii in a_max[j-d:j]),min(ii*a[j] for ii in a_min[j-d:j]))
_max[j]=temp_max #将最大值赋值给最终记录变量
_min[j]=temp_min #将最小值赋值给最终记录变量
a_max=_max
a_min=_min
print max(a_max) #直接输出最大能力值
从上述代码中我们就可以看出,我们不必每次都从最开始来遍历学生,因为能力值最大的部分一定在上一次遍历、比较厚的结果中出现,这样就大大简化了这个题的难度,当然我们需要考虑能力值为负的情况——因为能力值负数意味着对所求解提供负收益,那么我们就需要用同样的方法判断负值之后,求出最小(负)值,然后综合考虑。
2、 题目描述
现给出一个有n个正整数的数组A和一个整数sum,要求求出选择数组A中部分数字的和为sum的方案数。
那么,如果当两种选取方案存在有一个数字的下标不一样,我们就认为这是不同的组成方案。
例如:输入为两行:
第一行,两个正整数n(1 ≤ n ≤ 1000),sum(1 ≤ sum ≤ 1000)
第二行,n个正整数A[i](大小为32位整数),中间以空格隔开。
要求输出:所求的方案数量。
那么通过我们的分析,如果我们还是采用暴力拆解的方法,强行递归,无疑是耗时耗力的,并且数组的判断情况特别多,想要写出这些判断并且没有遗漏显然是很难的。
所以,采用如下的代码:(python3)
if __name__==’__main__’:
n, s=[int(i) for i in raw_input().split(‘ ‘)]
#在数组中不需要添加大于s的数
l=[int(i) for i in raw_input().split(‘ ‘) if int(i)0:
if j+l[i]<=s: #这一步是为了防止数组越界
temp[j+l[i]]+=result[j]
result=temp[:] #将最终结果以遍历的方法放入result
print result[s] #按要求输出最终结果
通过上面的代码,我们发现,在本题中,我们不需要一次又一次的计算数组然后进行比较,因为大于s的数字是不需要添加进目标数组的。有了这一步之后,王result中添加数组时,也就不用一次又一次地添加一个个“短组”了。
总而言之动态规划算法作为计算机算法中相当重要的方法之一,我们一定要有这个意识去掌握它和使用它,特别是在一个大问题是有多次从负的小问题构成的时候。但是这种算法其实是非常考验我们对问题的分析能力的,其实,在我们想清楚拆解的思路后,用代码来实现是非常容易的,特别是python这种库函数和辅助功能很多的语言来说,解决了思路问题,就等于解决了这个算法问题。