问题描述:
给定n级台阶,每次走1,2,3步,求解所有可走步骤
8个台阶,上台阶的人一次最多上3个台阶,问上这个8个台阶有多少种方法?
解题思路:
采用动态规划的思想,将大问题划分为若干个子问题,然后递归的解决每个可能的子问题。
首先,先考虑corner case:
1. 当只有一个台阶,那么有1种走法;
2. 当前只有两个台阶,那么有2种走法;
3. 当前有三级台阶,那么就存在4种走法;
接下来就要考虑如何划分子问题,在划分子问题的时候,存在三种情况,分别为:
1. 当前状态走一个台阶, 需要计算子问题 DP(n-1)
2. 当前状态走两个台阶, 需要计算子问题 DP(n-2)
3. 当前状态走三个台阶, 需要计算子问题 DP(n-3)
把三个子问题的解加起来就是当前问题的解。
台阶求解问题的状态转移公式为:
f(n)=f(n?1)+f(n?2)+f(n?3)
cornercase:f(1)=1,f(2)=2,f(3)=4
然后,写点代码吧。(人生苦短,选择python吧)
最简单的解决方式
首先采用最简单,最直接的动态规划思想进行解决:
def solution3(n):
‘‘‘
采用动态规划,递归调用
存在大量的重复子问题的计算
f(n) = f(n-1) + f(n-2) + f(n-3)
‘‘‘
# corner case
if n == 1: return 1
if n == 2: return 2
if n == 3: return 4
return solution3(n - 1) + solution3(n - 2) + solution3(n - 3)
这种最简单的动态规划,采用递归调用的方式计算子问题,但是会存在大量的重复子问题的计算。我们需要采用一些策略解决重复子问题的计算。
记录已经解决的子问题
为了解决重复子问题的计算,可以采用额外的存储空间记录已经解决了的子问题。
sub_problem = {}
def solution2(n):
‘‘‘
采用动态规划,但是记录已经解决了的自问题,不重复计算。
需要额外的存储空间。
‘‘‘
if n == 1: return 1
if n == 2: return 2
if n == 3: return 4
sub1 = None
if n - 1 in sub_problem:
sub1 = sub_problem[n-1]
else:
sub1 = solution2(n - 1)
sub2 = None
if n - 2 in sub_problem:
sub2 = sub_problem[n-2]
else:
sub2 = solution2(n - 2)
sub3 = None
if n - 3 in sub_problem:
sub3 = sub_problem[n-3]
else:
sub3 = solution2(n-3)
return sub1 + sub2 + sub3
采用Back Trace由小到大的解决问题
在动态规划中,经常采用Back Trace的方法由小问题逐步计算大的问题,这样可以不再采用递归调用,不重复计算子问题。
这是一种最优的求解方式。
def solution(n):
‘‘‘
采用动态规划,并且采用back trace从小问题向大问题回溯
没有重复计算的子问题
‘‘‘
so = {}
so[1] = 1
so[2] = 2
so[3] = 4
for idx in range(4, n+1):
so[idx] = so[idx-1] + so[idx-2] + so[idx-3]
return so[n]
获取所有可能的台阶走法
上面的三种方式只能计算最终的求解,也就是说只能计算给定n个台阶的情况下最终有多少种走法,但是不能给出每一种走法的过程。
如果要枚举所有的可能走法,这个时候就需要采用深度优先遍历的方式,每次选择一种走法,如果当前已经到达第n级台阶,则把当前的走法加入到枚举结果中,然后回溯到上一个状态,枚举其他的走法。
代码示例:
def get_all_combination(n):
‘‘‘
采用DFS获得所有可能的台阶的走法
上面提到的动态规划只能给出最终有多少中解,但是不能记录每一种走法, 如果想要记录每一种走法,可以采用DFS进行走法遍历。
‘‘‘
all_so = []
so = []
combination(n, so, all_so)
return all_so
def combination(n, so, all_so, total=0):
‘‘‘
采用DFS获得所有可能的台阶的走法
上面提到的动态规划只能给出最终有多少中解,但是不能记录每一种走法, 如果想要记录每一种走法,可以采用DFS进行走法遍历。
‘‘‘
if total == n:
copy = []
for item in so:
copy.append(item)
all_so.append(copy)
return
if total > n:
return
for stair in range(1, 4):
so.append(stair)
combination(n, so, all_so, total+stair)
so.pop()
上面的代码调用:
if __name__ == ‘__main__‘:
print solution3(8)
print solution3(4)
print ‘----------------------------------------------‘
print solution2(8)
print solution2(4)
print ‘----------------------------------------------‘
print solution(8)
print solution(4)
print ‘----------------------------------------------‘
print ‘all possible ways:‘
so = get_all_combination(8)
print len(so)
for s in so:
print s
获取所有的可能的走法的输出:
----------------------------------------------
all possible ways:
81
[1, 1, 1, 1, 1, 1, 1, 1]
[1, 1, 1, 1, 1, 1, 2]
[1, 1, 1, 1, 1, 2, 1]
[1, 1, 1, 1, 1, 3]
[1, 1, 1, 1, 2, 1, 1]
[1, 1, 1, 1, 2, 2]
[1, 1, 1, 1, 3, 1]
[1, 1, 1, 2, 1, 1, 1]
[1, 1, 1, 2, 1, 2]
[1, 1, 1, 2, 2, 1]
[1, 1, 1, 2, 3]
[1, 1, 1, 3, 1, 1]
[1, 1, 1, 3, 2]
[1, 1, 2, 1, 1, 1, 1]
[1, 1, 2, 1, 1, 2]
[1, 1, 2, 1, 2, 1]
[1, 1, 2, 1, 3]
[1, 1, 2, 2, 1, 1]
[1, 1, 2, 2, 2]
[1, 1, 2, 3, 1]
[1, 1, 3, 1, 1, 1]
[1, 1, 3, 1, 2]
[1, 1, 3, 2, 1]
[1, 1, 3, 3]
[1, 2, 1, 1, 1, 1, 1]
[1, 2, 1, 1, 1, 2]
[1, 2, 1, 1, 2, 1]
[1, 2, 1, 1, 3]
[1, 2, 1, 2, 1, 1]
[1, 2, 1, 2, 2]
[1, 2, 1, 3, 1]
[1, 2, 2, 1, 1, 1]
[1, 2, 2, 1, 2]
[1, 2, 2, 2, 1]
[1, 2, 2, 3]
[1, 2, 3, 1, 1]
[1, 2, 3, 2]
[1, 3, 1, 1, 1, 1]
[1, 3, 1, 1, 2]
[1, 3, 1, 2, 1]
[1, 3, 1, 3]
[1, 3, 2, 1, 1]
[1, 3, 2, 2]
[1, 3, 3, 1]
[2, 1, 1, 1, 1, 1, 1]
[2, 1, 1, 1, 1, 2]
[2, 1, 1, 1, 2, 1]
[2, 1, 1, 1, 3]
[2, 1, 1, 2, 1, 1]
[2, 1, 1, 2, 2]
[2, 1, 1, 3, 1]
[2, 1, 2, 1, 1, 1]
[2, 1, 2, 1, 2]
[2, 1, 2, 2, 1]
[2, 1, 2, 3]
[2, 1, 3, 1, 1]
[2, 1, 3, 2]
[2, 2, 1, 1, 1, 1]
[2, 2, 1, 1, 2]
[2, 2, 1, 2, 1]
[2, 2, 1, 3]
[2, 2, 2, 1, 1]
[2, 2, 2, 2]
[2, 2, 3, 1]
[2, 3, 1, 1, 1]
[2, 3, 1, 2]
[2, 3, 2, 1]
[2, 3, 3]
[3, 1, 1, 1, 1, 1]
[3, 1, 1, 1, 2]
[3, 1, 1, 2, 1]
[3, 1, 1, 3]
[3, 1, 2, 1, 1]
[3, 1, 2, 2]
[3, 1, 3, 1]
[3, 2, 1, 1, 1]
[3, 2, 1, 2]
[3, 2, 2, 1]
[3, 2, 3]
[3, 3, 1, 1]
[3, 3, 2]
版权声明:本文为博主原创文章,未经博主允许不得转载。