问题
一只青蛙一次可以在台阶跳一步或者两步,那么n级台阶青蛙有多少种跳法?
思路:
假设台阶为10级
- 青蛙最后一次跳上第10级台阶,只能从第9级或者第8级
- 这个问题就演变为 f(10) = f(9) + f(8)
- 依次类推f(9) = f(8) + f(7),一直到f(3) = f(2) + f(1) = 2 + 1 = 3种,即跳上3级台阶要3种方法
- 总结公式:f(n) = f(n-1) + f(n-2)
动态规划:
将复杂的问题逐步简单化,这就是动态规划思想
动态规划三要素:
1. 最优子结构
用动态规划求解最优化问题的第一步就是刻画最优解的结构,如果一个问题的解结构包含其子问题的最优解,就称此问题具有最优子结构性质
2. 边界
f(1) = 1,f(2) = 2这两个是问题的边界,如果没有边界,无法得出结果
3. 状态转移公式
状态转移公式就是定义了每一阶段与下一阶段的关系
代码实现Java版:
public class JumpStairs {
static Map<Integer, Integer> amap = new HashMap<>();
//使用递归,如果台阶为10,那么跳到10阶,可以从9阶也可以从8阶,根据动态规划,两个子问题解的和是最终解
private static int getWays(int target) {
if(target < 1) {
return 0;
}
if (target == 1 || target== 2) {
return target;
}
return getWays(target - 1) + getWays(target - 2);
}
//太浪费空间了,重复值多
private static int getWaysI(int target) {
if (target < 1) {
return 0;
}
if (target == 1 || target == 2) {
return target;
}
//使用Map缓存
if (amap.containsKey(target)) {
return amap.get(target);
} else {
int value = getWays(target - 1) + getWays(target - 2);
amap.put(target, value);
return value;
}
}
//使用动态规划
private static int getWaysII(int target) {
if (target < 1) {
return 0;
}
if (target == 1 || target == 2) {
return target;
}
int a = 1;
int b = 2;
int temp = 0; //临时变量用来迭代
for (int i = 3; i <= target; i++) {
temp = a + b;
a = b;
b = temp;
}
return temp;
}
public static void main(String[] args) {
System.out.println(getWays(10));
System.out.println(getWaysI(10));
System.out.println(getWaysII(10));
}
}
使用三种方法:
- 递归,这个思路比较简单
- 备忘录算法,自顶向下,使用Map缓存中间结果,这是因为在递归的过程中,会出现很多重复值,太占用空间和时间了,所以将求出的结果缓存起来,可以直接调用
- 自底向上,从头开始迭代, i=3开始 ,一直到 i=n 结束。每一次迭代,都会计算出多一级台阶的走法数量。迭代过程中只需保留两个临时变量a和b,分别代表了上一次和上上次迭代的结果,这样就避免了递归过程中的大量重复值
第三种方法才是真正的动态规划,其核心是记住已经解决过的子问题的解
代码实现Python版
'''
一只青蛙一次可以在台阶跳一步或者两步,那么n级台阶青蛙有多少种跳法
'''
# 使用递归
def getWaysI(n):
if n < 1:
return 0
if n == 1 or n == 2:
return n
return getWaysI(n-1) + getWaysI(n - 2)
# 画出递归树会出现很多重复值,使用字典缓存,备忘录算法
def getWaysII(n, cache):
if n < 1:
return 0
if n == 1 or n == 2:
return n
if n in cache:
return cache.get(n)
else:
value = getWaysII(n - 1, cache) + getWaysII(n - 2, cache)
cache[n] = value
return value
# 使用动态规划,就是记住已经解决过的子问题的解,从开始迭代
def getWaysIII(n):
if n < 1:
return 0
if n == 1 or n == 2:
return n
a = 1
b = 2
temp = 0
# 迭代
for i in range(3, n+1):
temp = a + b #记住子问题的解
a = b
b = temp
return temp
# 动态规划简化版
def getWaysIV(n):
arr = [1, 2]
if n < 1:
return 0
if n == 1 or n == 2:
return n
for i in range(3, n + 1):
# 先计算,再赋值,不使用临时变量temp
arr[0], arr[1] = arr[1], arr[0] + arr[1]
return arr[1]
if __name__ == '__main__':
cache = {}
print(getWaysII(10, cache))
print(getWaysI(10))
print(getWaysIII(10))
print(getWaysIV(10))