目录
Step 3. 递归方法 + 过程存储 实现 (top-down)
Step 4. 迭代方法 + 过程存储 实现 (bottom-up)
Step 5. 迭代方法 + 2个变量 Iterative + 2 variables (bottom-up)
背景
在From-good-to-great.-How-to-approach-most-of-DP-problems一文中,作者详细介绍了如何从递归思想转变到迭代思想的步骤。
There is some frustration when people publish their perfect fine-grained algorithms without sharing any information abut how they were derived. This is an attempt to change the situation. There is not much more explanation but it's rather an example of higher level improvements. Converting a solution to the next step shouldn't be as hard as attempting to come up with perfect algorithm at first attempt.
实现方法
通常,用以下5步可以实现:
- 确定递归关系
- 递归方法实现 (top-down)
- 递归方法 + 过程存储 实现 (top-down)
- 迭代方法 + 过程存储 实现 (bottom-up)
- 迭代方法 + N个变量 实现 (bottom-up)
Step 1. 确定递归关系
面对一间屋子,robber有2个选项:
- rob当前屋子i;
- 放弃rob当前屋子i.
如果选择‘1’,则意味着robber不能rob第i-1个屋子,但是可以安全的rob得到i-2以前所有屋子的最大loot;
如果选择‘2’,则意味着robber可以安全的rob得到i-1以前所以屋子的最大loot。
可见,问题转化选择何种情形收益最大化:
- i屋子的loot + i-2以前屋子的loot
- i-1屋子的loot + 更靠前的loot
rob(i) = max(rob(i - 2) + currentHouseValue, rob(i - 1))
Step 2. 递归方法实现 (top-down)
缺点:多次重复计算rob第i个屋子的结果
def rob_recursive(nums):
def robber(nums, i):
# 终止条件
if i<0:
return 0
# 递归公式
return max(robber(nums, i - 2) + nums[i], robber(nums, i - 1))
return robber(nums, len(nums) - 1)
Step 3. 递归方法 + 过程存储 实现 (top-down)
时间复杂度 O(n),空间复杂度 O(n)。后续可以去除递归栈,更好的优化算法。
def rob_recursive_memo(nums):
# 利用列表存储rob第i个屋子的结果
memo = [-1] * (len(nums))
def robber(nums, i):
# 终止条件
if i < 0:
return 0
if memo[i] >= 0:
return memo[i]
memo[i] = max(robber(nums, i - 2) + nums[i], robber(nums, i - 1))
return memo[i]
return robber(nums, len(nums) - 1)
Step 4. 迭代方法 + 过程存储 实现 (bottom-up)
def rob_Iterative_memo(nums):
if nums == []:
return 0
memo = [0] * (len(nums) + 1)
memo[1] = nums[0]
for i in range(1, len(nums)):
val = nums[i]
memo[i+1] = max(memo[i], memo[i-1] + val)
return memo[i+1]
Step 5. 迭代方法 + 2个变量 Iterative + 2 variables (bottom-up)
We can notice that in the previous step we use only memo[i]
and memo[i-1]
, so going just 2 steps back. We can hold them in 2 variables instead. This optimization is met in Fibonacci sequence creation and some other problems
在Step 4中,使用 memo[i]
和 memo[i-1]
这2个值就可以回溯前2步的结果。因此,只需要采用2个变量即可实现优化,大大降低空间复杂度。这种优化方式与Fibonacci序列的处理方式类似。
def rob_Iterative_2vars(nums):
if nums == []:
return 0
prev1 = 0 # rob第i个房间后,最大loot
prev2 = 0 # 前一次最大loot,也就是不rob第i个房间的最大loot
# for i in range(len(nums)):
# tmp = prev1
# prev1 = max(prev2 + nums[i], prev1)
# prev2 = tmp
for i in range(len(nums)):
prev1, prev2 = max(prev2 + nums[i], prev1), prev1
return prev1
附:在Fibonacci序列中使用的方法
def fib_Recursion(n):
n = input_check(n)
if n in (0, 1):
return 1
else:
return fib_Recursion(n-1)+fib_Recursion(n-2)
def fib_DynamicPlanning(n):
n = input_check(n)
array = [1]*(n+1)
for i in range(2,n+1):
array[i] = array[i-1] + array[i-2]
return array[n]
def fib_StatesCompressing(n):
f0 = 1
f1 = 1
if n in (0, 1):
return 1
else:
while n - 1 > 0:
f0, f1 = f1, f0 + f1
n -= 1
return f1