【dp_1】爬楼梯 | 斐波那契数 | 第 N 个泰波那契数 | 三步问题

动态规划

1. 核心思想

拆分重复子问题,保存中间结果避免重复计算。把大问题拆解成规模更小的子问题,子问题会反复出现;用数组 / 表格记录子问题答案,后续直接查表,不用重复递归暴力求解,大幅降低时间复杂度。

2. 两大必要条件

  1. 最优子结构大问题的最优解 = 若干更小子问题的最优解组合。例:最短路径、背包、最长递增子序列。
  2. 重叠子问题递归过程中会重复计算一模一样的子问题,缓存结果才有优化意义。

3. 两种实现方式

  1. **自顶向下(记忆化递归)**递归求解,遇到算过的子问题直接取缓存,没算过再计算并存起来。
  2. **自底向上(递推 DP 表,最常用)**从小规模子问题开始循环计算,逐步推到大问题,用 dp 数组存储状态。

4. 标准解题四步

  1. 定义 dp 数组含义:dp[i] 代表什么状态;
  2. 推导状态转移方程:当前状态如何由前面状态推出;
  3. 初始化边界条件:dp 0、dp 1 等初始值;
  4. 确定遍历顺序,循环填表,输出目标结果。

一.70. 爬楼梯

题目描述

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

每次你可以爬 12 个台阶。你有多少种不同的方法可以爬到楼顶呢?

示例 1:

复制代码
输入:n = 2
输出:2
解释:有两种方法可以爬到楼顶。
1. 1 阶 + 1 阶
2. 2 阶

示例 2:

复制代码
输入:n = 3
输出:3
解释:有三种方法可以爬到楼顶。
1. 1 阶 + 1 阶 + 1 阶
2. 1 阶 + 2 阶
3. 2 阶 + 1 阶

解题思路

想要到达第 x 阶楼梯,只有两种最后一步走法:

  1. 最后一步走 1 阶 :前面需要先走到第 x-1
  2. 最后一步走 2 阶 :前面需要先走到第 x-2

所以总走法满足公式:f(x) = f(x-1) + f(x-2)边界条件(递归出口):f(1) = 1f(2) = 2

普通暴力递归缺陷:大量重复计算,比如算 f (5) 要算 f (4)+f (3),算 f (4) 又要重复算 f (3),数值大了会超时。记忆化优化 :用数组 memo 记录每一个阶数对应的答案,计算过一次就存起来,下次需要直接查表,不再递归。

完整步骤:

  1. 创建 memo 数组,长度 n+1,下标对应楼梯阶数,初始全为 0(0 代表未计算)
  2. 定义递归函数 dfs (x):
    • 基线条件:x=1 返回 1,x=2 返回 2
    • 优化判断:如果 memo x≠0,代表之前算过,直接返回缓存值
    • 未计算:套用公式 memo[x] = dfs(x-1)+dfs(x-2),存入缓存再返回
  3. 调用 dfs (n) 从目标台阶开始递归求解

代码实现

Go 复制代码
func climbStairs(n int) int {
    //动态规划思路:用数组存已经算过的值
    memo := make([]int, n + 1)//缓存子问题结果
    var dfs func(x int) int;//先在作用域注册dfs这个函数变量名
    dfs = func(x int) int { //再实现递归调用
        if x == 1 {
            return 1
        }
        if x == 2 {
            return 2
        }
        if memo[x] != 0 {
            return memo[x] //直接查表,不用再递归
        }
        //递推公式:x阶的走法 = x-1阶走法 + x-2阶走法
        memo[x] = dfs(x - 1) + dfs(x - 2)
        return memo[x]
    }
    return dfs(n)
}

总结

本题使用动态规划中的记忆特性,暴力求解时递推f(n) = f(n - 1) + f(n - 2),会导致超时,使用记忆特性就可以查找memo,减少重复,减小时间复杂度。

二.509. 斐波那契数

题目描述

斐波那契数 (通常用 F(n) 表示)形成的序列称为 斐波那契数列 。该数列由 01 开始,后面的每一项数字都是前面两项数字的和。也就是:

复制代码
F(0) = 0,F(1) = 1
F(n) = F(n - 1) + F(n - 2),其中 n > 1

给定 n ,请计算 F(n)

示例 1:

复制代码
输入:n = 2
输出:1
解释:F(2) = F(1) + F(0) = 1 + 0 = 1

示例 2:

复制代码
输入:n = 3
输出:2
解释:F(3) = F(2) + F(1) = 1 + 1 = 2

示例 3:

复制代码
输入:n = 4
输出:3
解释:F(4) = F(3) + F(2) = 2 + 1 = 3

解题思路

递归会导致时间复杂度过大,所以可以用dp动态规划来解题

代码实现

Go 复制代码
func fib(n int) int {
    if n == 0 {
        return 0
    }
    dp := make([]int, n +  1)
    dp[0] = 0
    dp[1] = 1
    for i := 2; i <= n; i++ {
        dp[i] = dp[i - 1] + dp[i - 2]
    }
    return dp[n]
}

总结

递归,动态规划

三.1137. 第 N 个泰波那契数

题目描述

泰波那契序列 Tn 定义如下:

T0 = 0, T1 = 1, T2 = 1, 且在 n >= 0 的条件下 Tn+3 = Tn + Tn+1 + Tn+2

给你整数 n,请返回第 n 个泰波那契数 Tn 的值。

示例 1:

复制代码
输入:n = 4
输出:4
解释:
T_3 = 0 + 1 + 1 = 2
T_4 = 1 + 1 + 2 = 4

示例 2:

复制代码
输入:n = 25
输出:1389537

解题思路

和上一题几乎相同,递归思路+动态规划dp

代码实现

Go 复制代码
func tribonacci(n int) int {
    //dp
    if n == 0 {
        return 0
    }
    dp := make([]int, n + 1)
    dp[1] = 1
    if n >= 2 {
        dp[2] = 1
    }
    for i := 3; i <= n; i++ {
        dp[i] = dp[i - 1] + dp[i - 2]+ dp[i - 3]
    }
    return dp[n]
}

总结

利用dp缩减纯递归的时间复杂度

四.面试题 08.01. 三步问题

题目描述

三步问题。有个小孩正在上楼梯,楼梯有 n 阶台阶,小孩一次可以上 1 阶、2 阶或 3 阶。实现一种方法,计算小孩有多少种上楼梯的方式。结果可能很大,你需要对结果模 1000000007。

示例 1:

复制代码
 输入:n = 3 
 输出:4
 说明:有四种走法

示例 2:

复制代码
 输入:n = 5
 输出:13

提示:

  1. n 范围在1, 1000000之间

解题思路

利用动态规划,递推,先看最后一步可以走1/2/3阶,所以实现dpi = dpi-1 + dpi-2 + dpi-3

又由于初始条件

  • dp[1] = 1(1 阶只有 1 种走法)
  • dp[2] = 2(2 阶有 2 种走法:1+1、2)
  • dp[3] = 4(3 阶有 4 种走法)

实现走法相加

代码实现

Go 复制代码
func waysToStep(n int) int {
    const mod = 1000000007
    //看最后一步,上到n之前有三种情况
    //分别是n-1, n-2, n-3,对此进行倒推
    
    if n == 1 {
        return 1
    }
    if n == 2 {
        return 2
    }
    if n == 3 {
        return 4
    }
    
    //定义dp
    dp := make([]int, n)

    dp[0] = 1
    dp[1] = 2
    dp[2] = 4
    
    //从第4阶开始循环
    for i := 3; i < n; i++ {
        dp[i] = (dp[i - 1] + dp[i - 2] + dp[i - 3]) % mod
    }

    //返回第n阶的答案
    return dp[n - 1]
}

总结

动态规划+递推

相关推荐
星释1 小时前
鸿蒙智能体开发实战:35.鸿蒙壁纸大师 - 调用火山引擎模型生成壁纸
算法·华为·ai·harmonyos·鸿蒙·火山引擎
geovindu2 小时前
go: Floyd-Warshall Algorithms
开发语言·后端·算法·golang
db_murphy2 小时前
机器学习决策树的基尼系数是个啥?
学习·算法
2301_800256112 小时前
数据结构基础hw9判断选择题、hw7编程题、hw12编程题
数据结构·算法
拳里剑气2 小时前
C++算法:优先级队列
开发语言·c++·算法·优先级队列
Turbo正则2 小时前
机器学习入门笔记 | 基础算法及其应用场景
笔记·算法·机器学习
东华万里2 小时前
第37篇 手撕二叉树与堆的底层逻辑
数据结构·算法
知无不研2 小时前
算法:判断一个数是不是2的幂
数据结构·算法