蓝桥杯python备赛笔记之(八)动态规划(DP)

前言

整篇笔记共分为十章,都是博主在准备25年蓝桥杯时所写,听的网课是这个,讲的非常好,很适合零基础速成,如果有听不懂的可以多听几遍

https://www.bilibili.com/video/BV1Zs9VYrEgg?spm_id_from=333.788.videopod.sections\&vd_source=5edde23df276e6fb4a94821fa44f38b5

目录如下:

1.Python语法基础及算法入门

2.语法进阶&常用数据结构&算法入门

3.贪心&排序

4.哈希&暴力&前缀

5.二分查找&二分答案

6.搜索&BFS&DFS

7.[数据结构]并查集&堆

8.动态规划

9.图论

10.数论基础&日期问题

目录

前言

一、动态规划核心基础

[1. DP 的适用条件](#1. DP 的适用条件)

[2. DP 解题四步框架(蓝桥杯通用)](#2. DP 解题四步框架(蓝桥杯通用))

[步骤 1:状态定义](#步骤 1:状态定义)

[步骤 2:确定初始状态](#步骤 2:确定初始状态)

[步骤 3:推导状态转移方程](#步骤 3:推导状态转移方程)

[步骤 4:确定最终答案](#步骤 4:确定最终答案)

[3. DP 的两种实现方式](#3. DP 的两种实现方式)

(1)自底向上(迭代法,推荐)

(2)自顶向下(记忆化递归)

[4. DP 的空间优化(蓝桥杯高频技巧)](#4. DP 的空间优化(蓝桥杯高频技巧))

[二、蓝桥杯高频一维 DP 题型与模板](#二、蓝桥杯高频一维 DP 题型与模板)

[题型 1:斐波那契数列型(线性递推)](#题型 1:斐波那契数列型(线性递推))

问题特征

经典例题

解题步骤

[Python 模板(含空间优化)](#Python 模板(含空间优化))

[题型 2:最大子序和(子序列型)](#题型 2:最大子序和(子序列型))

问题特征

经典例题

解题步骤

[Python 模板(含空间优化)](#Python 模板(含空间优化))

[题型 3:打家劫舍(间隔选择型)](#题型 3:打家劫舍(间隔选择型))

问题特征

经典例题

解题步骤

[Python 模板(含空间优化)](#Python 模板(含空间优化))

[三、蓝桥杯高频二维 DP 题型与模板](#三、蓝桥杯高频二维 DP 题型与模板)

[题型 1:二维网格最短路径和(网格型 DP)](#题型 1:二维网格最短路径和(网格型 DP))

问题特征

经典例题

解题步骤

[Python 模板(含空间优化)](#Python 模板(含空间优化))

[题型 2:01 背包问题(背包型 DP,蓝桥杯重中之重)](#题型 2:01 背包问题(背包型 DP,蓝桥杯重中之重))

[子题型 1:01 背包(每个物品仅能选一次)](#子题型 1:01 背包(每个物品仅能选一次))

问题特征

解题步骤

[Python 模板(含空间优化,蓝桥杯必考)](#Python 模板(含空间优化,蓝桥杯必考))

[子题型 2:完全背包(每个物品可选无限次)](#子题型 2:完全背包(每个物品可选无限次))

问题特征

解题步骤

[Python 模板(优化版,蓝桥杯必考)](#Python 模板(优化版,蓝桥杯必考))

[题型 3:最长公共子序列(LCS,双序列型)](#题型 3:最长公共子序列(LCS,双序列型))

问题特征

解题步骤

[Python 模板](#Python 模板)

[四、蓝桥杯 DP 实战技巧与避坑指南](#四、蓝桥杯 DP 实战技巧与避坑指南)

[1. 状态定义的核心技巧](#1. 状态定义的核心技巧)

[2. 蓝桥杯高频避坑点](#2. 蓝桥杯高频避坑点)

(1)数组索引越界

(2)初始状态赋值错误

(3)背包问题遍历方向错误

(4)忽略空间限制

(5)递归深度超限(记忆化递归)

[3. 蓝桥杯 DP 刷题优先级](#3. 蓝桥杯 DP 刷题优先级)

[4. Python 优化 DP 运行速度的技巧](#4. Python 优化 DP 运行速度的技巧)

五、总结


论文投稿:
第三届边缘计算与并行、分布式计算国际学术会议(ECPDC 2026)

大会官网:https://ais.cn/u/qYFbY3

大会时间:2026年8月7-9日

大会地点:新加坡

一、动态规划核心基础

1. DP 的适用条件

必须同时满足以下两个条件,才适合用 DP 解决:

  • 重叠子问题:原问题拆解的子问题会被多次重复计算(暴力递归会做大量无用功,DP 用数组 / 字典存储子问题解);
  • 最优子结构:原问题的最优解可以由子问题的最优解推导而来(子问题的解独立,无后效性)。

无后效性:某一阶段的子问题解确定后,不受后续阶段的决策影响(即未来的状态不会改变过去的状态),是 DP 的关键前提。

2. DP 解题四步框架(蓝桥杯通用)

这是解决所有 DP 问题的核心思路,无论简单还是复杂题型,都按此步骤拆解,避免思路混乱:

步骤 1:状态定义

定义dp 数组 / 字典 的含义,是 DP 的灵魂,也是最关键的一步。

  • 格式:dp[i]/dp[i][j] 表示在某一条件下(i/j 的含义),问题的最优解 / 可行解 / 方案数
  • 维度:根据问题的决策维度确定,一维 DP 解决单状态决策问题,二维 DP 解决双状态决策问题(蓝桥杯以一维、二维为主,三维极少)。
  • 示例:dp[i]表示前 i 个元素的最大子序和;dp[i][j]表示从左上角走到第 i 行第 j 列的最短路径和。
步骤 2:确定初始状态

初始化 dp 数组的边界值 ,即子问题的最小解 (无需推导,直接确定的解),是 DP 的起点

  • 示例:dp[0] = 0(前 0 个元素和为 0);dp[0][j] = dp[0][j-1] + grid[0][j](第一行只能从左边走)。
步骤 3:推导状态转移方程

根据最优子结构 ,找到dp[i]前面子问题解 的关系,是 DP 的核心

  • 核心:当前状态 = 由前序状态通过决策推导而来
  • 示例:斐波那契数列dp[i] = dp[i-1] + dp[i-2];01 背包dp[j] = max(dp[j], dp[j-w[i]] + v[i])
步骤 4:确定最终答案

根据状态定义,确定 dp 数组的最终取值是原问题的解。

  • 示例:前 n 个元素的最大子序和答案为dp[n];从 (0,0) 到 (m-1,n-1) 的最短路径和答案为dp[m-1][n-1]

3. DP 的两种实现方式

蓝桥杯 Python 中常用两种实现方式,根据问题复杂度选择:

(1)自底向上(迭代法,推荐)

最小的子问题 开始推导,逐步计算出原问题的解,用循环 实现,效率高,无递归深度问题,是蓝桥杯首选方式

  • 特点:直接操作 dp 数组,符合 Python 的性能要求,适配大数据量;
  • 适用:所有 DP 题型,尤其是一维、二维 DP。
(2)自顶向下(记忆化递归)

原问题 开始,递归拆解为子问题,用字典 / 数组 存储已计算的子问题解(避免重复计算),本质是暴力递归 + 缓存

  • 特点:思路直观,符合人的思考逻辑,适合状态定义较复杂的问题;
  • 注意:Python 默认递归深度约 1000,若问题规模超过 1000,需用sys.setrecursionlimit() 扩大深度,否则会栈溢出。

4. DP 的空间优化(蓝桥杯高频技巧)

很多 DP 问题的状态转移仅依赖前 1/2 个状态 ,此时可通过滚动数组 / 变量 替代 dp 数组,将空间复杂度从O(n)/O(nm)优化为O(1)/O(n),适合蓝桥杯中空间限制较严格的题目。

  • 示例:斐波那契数列dp[i] = dp[i-1] + dp[i-2],可用两个变量a, b分别存储dp[i-2], dp[i-1],迭代更新;
  • 核心:只保留需要的前序状态,舍弃无用的历史状态

二、蓝桥杯高频一维 DP 题型与模板

一维 DP 是蓝桥杯最基础、考频最高的 DP 类型,状态定义为dp[i],仅一个决策维度,适合解决单序列、单阶段决策问题,以下是必考题型的模板和解析。

题型 1:斐波那契数列型(线性递推)

问题特征

状态转移仅依赖前 1/2 个连续的子状态 ,呈线性递推关系,是 DP 的入门题型,蓝桥杯常以爬楼梯、跳台阶为背景考察。

经典例题

爬楼梯:一次可以爬 1 级或 2 级台阶,求爬到第 n 级台阶的不同方法数。

解题步骤
  1. 状态定义:dp[i]表示爬到第 i 级台阶的方法数;
  2. 初始状态:dp[1] = 1(1 级台阶 1 种方法),dp[2] = 2(2 级台阶 2 种方法);
  3. 转移方程:dp[i] = dp[i-1] + dp[i-2](最后一步爬 1 级 / 2 级,由前 i-1 或 i-2 级推导);
  4. 最终答案:dp[n]
Python 模板(含空间优化)
python 复制代码
# 基础版:空间O(n)
def climbStairs(n):
    if n <= 2:
        return n
    dp = [0] * (n + 1)
    dp[1], dp[2] = 1, 2
    for i in range(3, n + 1):
        dp[i] = dp[i-1] + dp[i-2]
    return dp[n]

# 优化版:空间O(1)(蓝桥杯推荐)
def climbStairs(n):
    if n <= 2:
        return n
    a, b = 1, 2  # a=dp[i-2], b=dp[i-1]
    for i in range(3, n + 1):
        a, b = b, a + b
    return b

题型 2:最大子序和(子序列型)

问题特征

求数组中连续子序列 的最优解(最大和 / 最小和),状态转移依赖前一个状态的决策(选 / 不选当前元素),蓝桥杯常直接考察或结合其他考点。

经典例题

最大子序和:给定一个整数数组,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

解题步骤
  1. 状态定义:dp[i]表示以第 i 个元素结尾的连续子数组的最大和;
  2. 初始状态:dp[0] = nums[0](以第一个元素结尾的子数组和为自身);
  3. 转移方程:dp[i] = max(nums[i], dp[i-1] + nums[i])(选:与前一个子数组拼接;不选:以当前元素为新子数组起点);
  4. 最终答案:max(dp)(遍历所有结尾的子数组,取最大值)。
Python 模板(含空间优化)
python 复制代码
# 基础版:空间O(n)
def maxSubArray(nums):
    n = len(nums)
    dp = [0] * n
    dp[0] = nums[0]
    for i in range(1, n):
        dp[i] = max(nums[i], dp[i-1] + nums[i])
    return max(dp)

# 优化版:空间O(1)(蓝桥杯推荐)
def maxSubArray(nums):
    pre = nums[0]  # 存储dp[i-1]
    res = pre      # 存储全局最大值
    for i in range(1, len(nums)):
        pre = max(nums[i], pre + nums[i])
        res = max(res, pre)
    return res

题型 3:打家劫舍(间隔选择型)

问题特征

从序列中选择元素,不能选择相邻元素 ,求最优解(最大和 / 最大价值),蓝桥杯常以打家劫舍、取数游戏为背景考察。

经典例题

打家劫舍:沿街有一排房屋,每个房屋有一定现金,不能偷相邻的房屋,求能偷到的最大现金数。

解题步骤
  1. 状态定义:dp[i]表示前 i 个房屋能偷到的最大现金数;
  2. 初始状态:dp[0] = 0dp[1] = nums[0](1 个房屋只能偷它);
  3. 转移方程:dp[i] = max(dp[i-1], dp[i-2] + nums[i-1])(不偷第 i 个:取前 i-1 个的解;偷第 i 个:取前 i-2 个的解 + 当前房屋现金);
  4. 最终答案:dp[n](n 为房屋总数)。
Python 模板(含空间优化)
python 复制代码
# 基础版:空间O(n)
def rob(nums):
    n = len(nums)
    if n == 0:
        return 0
    dp = [0] * (n + 1)
    dp[1] = nums[0]
    for i in range(2, n + 1):
        dp[i] = max(dp[i-1], dp[i-2] + nums[i-1])
    return dp[n]

# 优化版:空间O(1)(蓝桥杯推荐)
def rob(nums):
    if not nums:
        return 0
    a, b = 0, nums[0]  # a=dp[i-2], b=dp[i-1]
    for num in nums[1:]:
        a, b = b, max(b, a + num)
    return b

三、蓝桥杯高频二维 DP 题型与模板

二维 DP 是蓝桥杯中档题核心考点 ,状态定义为dp[i][j],有两个决策维度,适合解决双序列、二维网格、多条件决策问题,以下是必考题型的模板和解析,覆盖 90% 的蓝桥杯二维 DP 考题。

题型 1:二维网格最短路径和(网格型 DP)

问题特征

二维矩阵 / 网格 中,从一个点走到另一个点(仅能上下 / 左右 / 斜向走,蓝桥杯多为向右 / 向下),求路径的最优解(最短和 / 最长和 / 方案数),是蓝桥杯最常考的二维 DP 题型。

经典例题

最小路径和:给定一个非负整数的 m x n 网格,从左上角走到右下角,仅能向右或向下走,求路径上的数字之和的最小值。

解题步骤
  1. 状态定义:dp[i][j]表示从左上角走到第 i 行第 j 列的最小路径和;
  2. 初始状态:
    • 第一行:只能从左边走,dp[0][j] = dp[0][j-1] + grid[0][j]
    • 第一列:只能从上面走,dp[i][0] = dp[i-1][0] + grid[i][0]
  3. 转移方程:dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + grid[i][j](从上面 / 左边走,取最小值 + 当前网格值);
  4. 最终答案:dp[m-1][n-1](网格索引从 0 开始)。
Python 模板(含空间优化)
python 复制代码
# 基础版:空间O(mn)
def minPathSum(grid):
    m, n = len(grid), len(grid[0])
    # 初始化dp数组
    dp = [[0]*n for _ in range(m)]
    dp[0][0] = grid[0][0]
    # 初始化第一行
    for j in range(1, n):
        dp[0][j] = dp[0][j-1] + grid[0][j]
    # 初始化第一列
    for i in range(1, m):
        dp[i][0] = dp[i-1][0] + grid[i][0]
    # 状态转移
    for i in range(1, m):
        for j in range(1, n):
            dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + grid[i][j]
    return dp[m-1][n-1]

# 优化版:空间O(n)(用一维数组替代二维,蓝桥杯推荐)
def minPathSum(grid):
    m, n = len(grid), len(grid[0])
    dp = [0] * n
    # 初始化第一行
    dp[0] = grid[0][0]
    for j in range(1, n):
        dp[j] = dp[j-1] + grid[0][j]
    # 状态转移
    for i in range(1, m):
        # 初始化当前行第一列
        dp[0] += grid[i][0]
        for j in range(1, n):
            dp[j] = min(dp[j], dp[j-1]) + grid[i][j]
    return dp[-1]

题型 2:01 背包问题(背包型 DP,蓝桥杯重中之重)

背包问题是蓝桥杯DP 的核心考点 ,分为01 背包、完全背包、多重背包 ,其中01 背包是基础,完全背包由 01 背包变形而来,多重背包考频较低,以下重点讲解 01 背包和完全背包。

子题型 1:01 背包(每个物品仅能选一次)
问题特征

有 N 个物品,每个物品有重量 w [i]价值 v [i] ,一个容量为 V 的背包,每个物品最多选一次 ,求背包能装的最大价值

解题步骤
  1. 状态定义:dp[i][j]表示前 i 个物品,背包容量为 j 时,能装的最大价值;
  2. 初始状态:dp[0][j] = 0(0 个物品,价值为 0),dp[i][0] = 0(容量为 0,价值为 0);
  3. 转移方程:
    • 不选第 i 个物品:dp[i][j] = dp[i-1][j]
    • 选第 i 个物品(需容量≥w [i]):dp[i][j] = dp[i-1][j-w[i]] + v[i]
    • 最终:dp[i][j] = max(不选, 选)
  4. 最终答案:dp[N][V]
Python 模板(含空间优化,蓝桥杯必考)

空间优化核心 :二维 dp 可优化为一维数组 dp [j] ,容量从后往前遍历 (避免重复选择同一物品),空间复杂度从O(NV)优化为O(V),是蓝桥杯必写优化

python 复制代码
# 01背包优化版:一维数组+逆序遍历,空间O(V)(蓝桥杯推荐)
def zero_one_knapsack(V, w, v):
    n = len(w)
    dp = [0] * (V + 1)  # dp[j]表示容量j的背包的最大价值
    for i in range(n):  # 遍历每个物品
        # 逆序遍历容量,避免重复选同一物品
        for j in range(V, w[i]-1, -1):
            dp[j] = max(dp[j], dp[j - w[i]] + v[i])
    return dp[V]

# 使用示例
V = 5  # 背包容量
w = [2, 3, 4]  # 物品重量
v = [3, 4, 5]  # 物品价值
print(zero_one_knapsack(V, w, v))  # 输出7(选第一个和第二个物品,2+3=5容量,3+4=7价值)
子题型 2:完全背包(每个物品可选无限次)
问题特征

与 01 背包唯一区别:每个物品可选无限次,蓝桥杯常以 ** 凑零钱、爬楼梯(进阶)** 为背景考察。

解题步骤

与 01 背包基本一致,仅状态转移的容量遍历方向不同

Python 模板(优化版,蓝桥杯必考)

空间优化核心 :一维数组dp[j],容量从前往后遍历(允许重复选择同一物品),是与 01 背包的唯一区别!

python 复制代码
# 完全背包优化版:一维数组+正序遍历,空间O(V)(蓝桥杯推荐)
def complete_knapsack(V, w, v):
    n = len(w)
    dp = [0] * (V + 1)
    for i in range(n):  # 遍历每个物品
        # 正序遍历容量,允许重复选同一物品
        for j in range(w[i], V + 1):
            dp[j] = max(dp[j], dp[j - w[i]] + v[i])
    return dp[V]

# 经典例题:凑零钱(求凑成总金额的最少硬币数)
def coinChange(coins, amount):
    # 初始化dp为无穷大,表示不可达
    dp = [float('inf')] * (amount + 1)
    dp[0] = 0  # 金额0需要0个硬币
    for coin in coins:
        for j in range(coin, amount + 1):
            dp[j] = min(dp[j], dp[j - coin] + 1)
    return dp[amount] if dp[amount] != float('inf') else -1

题型 3:最长公共子序列(LCS,双序列型)

问题特征

给定两个字符串 / 序列,求它们的最长公共子序列(子序列可不连续),是蓝桥杯双序列 DP 的代表题型,常直接考察或结合字符串编辑距离。

解题步骤
  1. 状态定义:dp[i][j]表示字符串 s1 的前 i 个字符和字符串 s2 的前 j 个字符的最长公共子序列长度;
  2. 初始状态:dp[0][j] = 0dp[i][0] = 0(一个字符串为空,公共子序列长度为 0);
  3. 转移方程:
    • 若 s1 [i-1] == s2 [j-1](当前字符相同):dp[i][j] = dp[i-1][j-1] + 1
    • 若 s1 [i-1] != s2 [j-1](当前字符不同):dp[i][j] = max(dp[i-1][j], dp[i][j-1])
  4. 最终答案:dp[len(s1)][len(s2)]
Python 模板
python 复制代码
def longestCommonSubsequence(s1, s2):
    m, n = len(s1), len(s2)
    # 初始化dp数组,多开一行一列方便处理边界
    dp = [[0]*(n+1) for _ in range(m+1)]
    for i in range(1, m+1):
        for j in range(1, n+1):
            if s1[i-1] == s2[j-1]:
                dp[i][j] = dp[i-1][j-1] + 1
            else:
                dp[i][j] = max(dp[i-1][j], dp[i][j-1])
    return dp[m][n]

# 使用示例
s1 = "abcde"
s2 = "ace"
print(longestCommonSubsequence(s1, s2))  # 输出3(ace)

四、蓝桥杯 DP 实战技巧与避坑指南

1. 状态定义的核心技巧

  • 从问题出发 :让 dp 数组的含义直接对应问题的解,避免复杂的状态转换;
  • 关注 "结尾 / 前 i 个" :一维 DP 常定义为以i结尾前i个,二维 DP 常定义为前i个+前j个/第i行第j列
  • 维度简化:能定义一维的绝不定义二维,减少代码量和空间复杂度。

2. 蓝桥杯高频避坑点

(1)数组索引越界
  • 原因:蓝桥杯题目中元素编号常从 1 开始,而 Python 列表索引从 0 开始;
  • 解决:初始化 dp 数组时多开一个位置(如前 n 个元素,dp 数组长度为 n+1),避免索引错位。
(2)初始状态赋值错误
  • 问题:最值问题中初始状态赋值为 0,导致负数解错误(如最大子序和含负数);
  • 解决:
    • 最大值 :非边界初始状态赋值为-float('inf')
    • 最小值 :非边界初始状态赋值为float('inf')
    • 计数问题:初始状态赋值为 0,不可达状态赋值为 0(或根据题意)。
(3)背包问题遍历方向错误
  • 01 背包必须逆序遍历容量,否则会重复选择同一物品;
  • 完全背包必须正序遍历容量,否则无法重复选择同一物品;
  • 这是蓝桥杯背包问题最常见的丢分点,务必牢记!
(4)忽略空间限制
  • 蓝桥杯部分题目空间限制严格(如 128MB),二维 DP 数组可能超出空间;
  • 解决:优先使用空间优化版(一维数组 / 滚动变量),尤其是背包问题、网格 DP。
(5)递归深度超限(记忆化递归)
  • Python 默认递归深度为 1000,若问题规模超过 1000(如 n=1e4),递归会报错;

  • 解决:① 改用自底向上的迭代法 (推荐);② 手动扩大递归深度:

    python 复制代码
    import sys
    sys.setrecursionlimit(10**6)  # 扩大到1e6

3. 蓝桥杯 DP 刷题优先级

根据考频从高到低刷题,高效备战:

  1. 基础一维 DP:爬楼梯、最大子序和、打家劫舍;
  2. 背包问题:01 背包、完全背包(凑零钱、找方案数);
  3. 二维网格 DP:最短路径和、不同路径(求方案数);
  4. 双序列 DP:最长公共子序列、最长回文子串;
  5. 进阶 DP:状态压缩 DP(如状压 01 背包)、数位 DP(蓝桥杯国赛偶尔考)。

4. Python 优化 DP 运行速度的技巧

蓝桥杯 Python 的时间限制较严格(通常 1s),DP 题若数据量较大(如 1e4、1e5),需优化运行速度:

  1. 用列表替代字典:dp 的存储优先用列表,字典的查找和赋值效率远低于列表;
  2. 减少循环内的计算:将循环内的重复计算提取到外部(如提前计算 len (nums));
  3. 用局部变量替代全局变量:Python 局部变量的访问速度比全局变量快;
  4. 避免不必要的嵌套:尽量简化循环结构,减少嵌套层数。

五、总结

动态规划的核心不是死记模板,而是理解解题框架 ------状态定义→初始状态→转移方程→最终答案 ,所有题型都是这个框架的具体应用。蓝桥杯的 DP 题难度适中,以基础和中档题为主,极少出现难题,只要熟记核心题型的模板,掌握空间优化技巧,避开常见坑点,就能轻松拿下 DP 的分数。

备考建议:先刷透经典题型,再做蓝桥杯真题,将模板内化为自己的思路,做到能根据题目快速拆解状态、推导转移方程,这是解决 DP 问题的关键。

相关推荐
weixin199701080162 小时前
货铺头商品详情页前端性能优化实战
java·前端·python
深蓝电商API2 小时前
爬虫监控告警:结合企业微信或钉钉,打造 7×24 小时实时预警系统
爬虫·python·钉钉·企业微信
whycthe2 小时前
c++动态规划算法详解
c++·算法·动态规划
懷淰メ2 小时前
python3GUI--socket+PyQt5开发局域网微信(含功能、详细介绍、分享)
python·学习·gui·大学生·pyqt5·微信界面
risc1234562 小时前
channel.read(dest, channelPosition) 的读取大小限制
开发语言·python
不想看见4042 小时前
Single Number位运算基础问题--力扣101算法题解笔记
数据结构·算法
靠沿2 小时前
【优选算法】专题十二——栈
算法
ByNotD0g2 小时前
Doris 学习笔记
android·笔记·学习
困死,根本不会2 小时前
Qt Designer 基础操作学习笔记
开发语言·笔记·qt·学习·microsoft