【动态规划Ⅰ】斐波那契、爬楼梯、杨辉三角

什么是动态规划

动态规划是一种通过将原问题分解为相对简单的子问题来解决复杂问题的方法。基本思想是递归地将一个复杂的问题划分为许多更简单的子问题,存储这些子问题的每个子问题的解,并最终将存储的答案用于解决原始问题。通过缓存子问题的解,动态规划有时可以避免指数级的浪费。它通常用于优化问题,其中需要找到最佳解决方案。动态规划算法通常用于解决具有重叠子问题和最优子结构性质的问题。

  • 重叠子问题:原问题可以被分解为相同的子问题。这意味着在解决原问题时,我们可能多次解决相同的子问题。
  • 最优子结构:问题的最优解可以通过其子问题的最优解来求解。

通常,使用动态规划解决问题的步骤包括以下几个方面:

  • 定义子问题:将原问题分解为子问题。
  • 解决子问题:解决子问题并将结果存储起来,通常使用数组或类似的数据结构来存储子问题的解,以避免重复计算。
  • 合并子问题的解:利用子问题的解构建原问题的解。
  • 递归或迭代:通常使用递归或迭代的方式来解决问题。

斐波那契数组相关题目

斐波那契数组是典型的动态规划问题,可以从存储的子问题答案扩展到要求解的大问题。

509. 斐波那契数 Easy

509. 斐波那契数

斐波那契数 (通常用 F(n) 表示)形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是:
F(0) = 0,F(1) = 1
F(n) = F(n - 1) + F(n - 2),其中 n > 1
给定 n ,请计算 F(n) 。

其实可以直接用一个数组,dp[i]就对应F(i),但实际上我们并不需要整个数组,只需要F(n),而F(n)只和F(n-1)和F(n-2)相关,因此直接用两个变量存储即可。更新过程就是F(n) = F(n - 1) + F(n - 2)。Java代码如下:

class Solution {
    public int fib(int n) {
        if(n <= 1)
            return n;
        int f0 =0, f1 = 1;
        for(int i = 2; i <= n; i++){
            int cur = f0 + f1;
            f0 = f1;
            f1 = cur;
        }
        return f1;
    }
}

1137. 第 N 个泰波那契数 Easy

1137. 第 N 个泰波那契数

泰波那契序列 Tn 定义如下:
T0 = 0, T1 = 1, T2 = 1, 且在 n >= 0 的条件下 Tn+3 = Tn + Tn+1 + Tn+2
给你整数 n,请返回第 n 个泰波那契数 Tn 的值。

这个和上面的斐波那契基本一样,只是当前状态由前面三个状态决定,而不是前面两个。用三个变量分别存储Tn、Tn+1、Tn+2,按照 Tn+3 = Tn + Tn+1 + Tn+2逐步更新即可。 Java代码如下:

    public int tribonacci(int n) {
        if(n <= 1)
            return n;
        if (n == 2)
            return 1;
        
        int f0 = 0, f1 = 1, f2 = 1;
        for(int i = 3; i <= n; i++){
            int cur = f0 + f1 + f2;
            f0 = f1;
            f1 = f2;
            f2 = cur;
        }
        return f2;
    }
}

杨辉三角

118. 杨辉三角 Easy

118. 杨辉三角

给定一个非负整数 numRows,生成「杨辉三角」的前 numRows 行。
在「杨辉三角」中,每个数是它左上方和右上方的数的和。
在这里插入图片描述

其实斐波那契和杨辉三角,都是数学里面学过的,更新过程已经很清晰了。 上面的图这个三角形,如果变成数组左下角直角的形式,对于每一行,dp[i][j] = dp[i-1][j-1] + dp[i-1][j]。Java代码如下:

class Solution {
    public List<List<Integer>> generate(int numRows) {
        List<List<Integer>> triangle = new ArrayList<>();   
        for(int i = 0; i < numRows; i++){
            List<Integer> newRow = new ArrayList<>();            
            for(int j = 0; j <= i; j++){
                if(j==0 || j==i)
                    newRow.add(1);
                else
                    newRow.add(triangle.get(i-1).get(j) + triangle.get(i-1).get(j-1));
            }           
            triangle.add(newRow);
        }
        return triangle;        
    }
}

爬楼梯相关题目

70. 爬楼梯 Easy

70. 爬楼梯

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

根据题目,到第n阶台阶,可以从n-1跨一步到;可以从n-2跨两步到。因此动态规划的状态转移:dp[n] = dp[n-1] + dp[n-2]。实际上只需要存储dp[n-1]和dp[n-2]这两个变量即可。Java代码如下:

class Solution {
    public int climbStairs(int n) {
        if(n <=2 )
            return n;
        int stair1 =1, stair2 = 2;
        for(int i = 3; i <= n; i++){
            int cur = stair1 + stair2;
            stair1 = stair2;
            stair2 = cur;
        }
        return stair2;    
    }
}

746. 使用最小花费爬楼梯 Easy

746. 使用最小花费爬楼梯

给你一个整数数组 cost ,其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。
你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。
请你计算并返回达到楼梯顶部的最低花费。
示例 1:
输入:cost = [10,15,20]
输出:15
解释:你将从下标为 1 的台阶开始。支付 15 ,向上爬两个台阶,到达楼梯顶部。总花费为 15 。

上一个题目是直接求方法总和,这个题目是最小最大问题。 但本质上两个题一样的,因此到当前第i个台阶的代价,取决与i-1和i-2。因为在i-1个台阶上付出cost[i-1]的费用,选择向上一个台阶就到了第i阶;同样在第i-2个台阶上付出cost[i-2]的费用,选择向上两个台阶就到了第i个台阶。因为是最小值,因此dp[i] = min(dp[i-2] + cost[i-2], dp[i-1] + cost[i-1])。由于只和dp[i-1]、dp[i-2]两个状态相关,因此可以优化成只用两个变量存储,Java代码如下:

class Solution {
    public int minCostClimbingStairs(int[] cost) {
        int n = cost.length;
        if(n == 2)
            return Math.min(cost[0],cost[1]);
        int minCost1 = 0, minCost2 = 0;
        for(int i = 2; i <= n ; i++){
            int newCost = Math.min(minCost1 + cost[i-2], minCost2 + cost[i-1]);
            minCost1 = minCost2;
            minCost2 = newCost;
        }
        return minCost2;
    }
}
  • 11
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,针对您的问题,我来讲一下动态规划爬楼梯问题5个台阶的分析方法。 在动态规划中,我们通常会使用一个数组来存储子问题的解,以便后续的子问题可以直接使用已解决的子问题的解,从而避免重复计算。对于爬楼梯问题,我们可以使用一个一维数组来存储到达每个台阶的不同路径数量。 以下是对于5个台阶的分析方法: 1. 递推法:根据题目可知,到达第n个台阶的方法数为到达第n-1和第n-2个台阶的方法数之和,因此可以使用递推的方式来求解。具体实现可以使用一个一维数组dp来存储到达每个台阶的方法数,从dp[0]开始逐个计算,最终返回dp[n]即可。 2. 递归法:类似于递推法,也是通过递归的方式来求解。递归终止条件为到达第0个台阶和第1个台阶的方法数分别为1和1,递归求解到达n-1和n-2个台阶的方法数,最终返回它们之和即可。 3. 记忆化搜索法:在递归法的基础上,加入了记忆化的思想,即在求解每个子问题时,先查看该子问题是否已经计算过,如果已经计算过,则直接返回已有的解。如果没有计算过,则递归求解,并将结果存储到一个数组中,以便后续的子问题可以直接使用已解决的子问题的解。 4. 斐波那契数列法:利用斐波那契数列的递推公式,即f(n)=f(n-1)+f(n-2),来求解爬楼梯问题。具体实现可以使用两个变量f1和f2来存储f(n-1)和f(n-2)的值,然后依次更新它们的值,最终返回f(n)即可。 5. 矩阵快速幂法:在斐波那契数列法的基础上,利用矩阵快速幂的思想,可以将时间复杂度从O(n)降低到O(logn)。具体实现可以将斐波那契数列的递推公式转化为矩阵的形式,然后使用矩阵快速幂的方式来求解。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值