相信很多童鞋在上学的时候虽然学习过动态规划,但是实际上对动态规划还是模糊的,其实,动态规划本质依然是递归算法,只不过是满足特定条件的递归算法。下面以LintCode343题为例,详细分析,如何使用动态规划一步一步求解,帮助大家了解动态规划的概念:
整数拆分:
给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化。 返回你可以获得的最大乘积。
示例:
输入: 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1。
分析:
首先我们先看假如对4进行分割的问题,那么可以分割成 1+3、2+2、3+1的问题,然后去最大的那个分割
同理对,下层的3和2进行分割:
我们可以看到蓝色底纹的就是重叠子问题,更一般的,可以分割为1+n-1、2+n-2、3+n-3、、、、n-1+ 1
可以看出存在大量的重叠子问题,这就意味着我们可以使用记忆化搜索。
下面我们进行编码具体解决:
class Solution(object):
def breakInteger(self,n,memo):
'''
记忆搜索处理最大分割 lintcode343
:return:
'''
if n == 1:
return 1
res = -1
for i in range(1,n):
res = max(res,i*(n-i),i * self.breakInteger(n-i,memo))
return res
def integerBreak(self, n):
return self.breakInteger(n,memo)
上述的代码就一个递归过程,我们说了存在大量重复子问题的计算,所以为了减少计算机的重复计算。我们采用记忆化搜索,就是采用一个list存储每次的最优分割,具体如下:
class Solution(object):
def breakInteger(self,n,memo):
'''
记忆搜索处理最大分割 lintcode343
:return:
'''
if n == 1:
return 1
if memo[n] != -1:
return memo[n]
res = -1
for i in range(1,n):
res = max(res,max(i*(n-i),i * self.breakInteger(n-i,memo)))
memo[n] = res
return res
def integerBreak(self, n):
memo = [-1 for _ in range(n + 1)]
return self.breakInteger(n,memo)
上述就是,记忆化搜索的过程。一般的,能使用记忆化搜索的,我们也可能使用动态规划来接,记忆化搜索是自顶向下的,二动态规划是自底向上的,代码如下:
class Solution(object):
def dp_integerBreak(self, n):
'''
动态规划解最大分割 lintcode343
:param n:
:return:
'''
memo = [-1 for _ in range(n + 1)]
memo[1] = 1
for i in range(2,n+1):
for j in range(1,i):
memo[i] = max(memo[i],j*(i-j),j+memo[i-j])
return memo[n]
LeetCode上还有类似的题,279、 62、 63、198可以自己去练习练习
198题分析: