理解动态规划算法

问题

一只青蛙一次可以在台阶跳一步或者两步,那么n级台阶青蛙有多少种跳法?

思路:

假设台阶为10级

  1. 青蛙最后一次跳上第10级台阶,只能从第9级或者第8级
  2. 这个问题就演变为 f(10) = f(9) + f(8)
  3. 依次类推f(9) = f(8) + f(7),一直到f(3) = f(2) + f(1) = 2 + 1 = 3种,即跳上3级台阶要3种方法
  4. 总结公式: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));
    }
}

使用三种方法:

  1. 递归,这个思路比较简单
  2. 备忘录算法,自顶向下,使用Map缓存中间结果,这是因为在递归的过程中,会出现很多重复值,太占用空间和时间了,所以将求出的结果缓存起来,可以直接调用
  3. 自底向上,从头开始迭代, 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))
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值