动态规划(Dynamic Programming)是一种解决一类最优化问题的算法思想。最基本的方法思想就是将复杂问题分解为若干子问题,通过综合子问题的最优解来得到原问题的最优解。一般分为递归写法与递推写法。
值得注意的是,一个问题必须拥有重叠子问题和最优子结构才能用动态规划解决。重叠子问题是指复杂问题是由重复进行的子问题来解决实现的,也就是重复一定的步骤才能完成目标问题;最优子结构是指所要求得的复杂问题的最优解,是能够由子问题的最优解构造的,也就是分解后的子问题的最优解决方案,是最后复杂问题最优解结构的一部分。
例如用几种价值的物品来凑单,要使物品数量最少。这是一道经典的动态规划问题,一般的常规思想就是尽量去选贵的物品,这是贪心思想,但比如物品价值为2、5、7,要恰好凑27元。仔细分析下就可以看出贪心的整体局限性。贪心解为7+7+7+2+2+2,最终最优解应为7+5+5+5+5。
一般分为四步:
确定状态。略微有一点抽象。统观整个问题,去思考问题得出答案的最后一步,从而在思维上开始分解一步步得到答案的过程,进而去帮助我们将复杂问题分解为子问题。分解为子问题是解决动态规划问题的关键。对于例题分析,要凑27元,最后一步是27=2/5/7+x,进而将问题转为凑x元的最少物品数,进而实现问题状态的转变。
写出状态转移方程。这步是动态规划问题的重中之重,将复杂堆叠的问题分解为子问题,再找到每一步实现所需的运算逻辑。对于例题分析, 将凑某个值的最小物品数记录为dp[x],以27为例,dp[27]=min{dp[25],dp[22],dp[20]} +1 ,这应该不难理解,即凑20、22、25的最小物品数中最小的数量再加1即为凑27的最小物品数。想明白这件事,就已经完成了对于例题的问题分解。然后我们将这个式子进行推广,就得到了问题的状态转移方程:dp[x]=min{dp[x-2],dp[x-5],dp[x-7]}+1
确定初始条件和边界条件。考虑问题终止的条件,比如将dp[-7]到dp[-1]均设为正无穷,进而dp[x]为正无穷就表示x无法凑出。考虑问题的初始条件,例如dp[0]=0。
根据实际问题确定计算顺序,比如例题应从0开始计算,一直算到27,但也有不同的问题需要不同的计算顺序,应具体问题具体分析。
应在实际问题中感受并领悟动态规划,我也会继续用一些实际算法题来练习解决动态规划问题。