算法讲解088【必备】动态规划专题总结与预告

递归和动态规划专题(一)----剑指offer+左程云算法 递归和动态规划专题(一)---剑指offer+左程云算法(一).斐波那契专题 阅读详情

     b站链接:左程云的个人空间-左程云个人主页-哔哩哔哩视频

GitHub链接:algorithmzuo (左程云) · GitHub

解题流程:

1.尝试,进行逻辑分析,分类讨论,观察规律,从简单到复杂,正难求反,利用答案的集合性质等等,观察数据量。

2.写出递归

3.挂缓存,改成记忆化搜索

4.根据记忆化搜索写出严格位置依赖版本的dp

5.画图,举例子,建立空间感,构建空间压缩版本。

常见dp

1.背包dp

背包动态规划(DP)是动态规划中的经典问题,其核心在于在有限容量的背包中选择物品以达到最优解(如最大价值、最小成本等)。以下是常见的背包问题类型及其解法总结:


​​1. 0-1 背包问题​​

​​问题描述​​:

  • 给定物品的重量 w[i] 和价值 v[i],每个物品只能选或不选(0或1),求容量为 W 的背包能装的最大价值。

​​解法​​:

  • ​​状态定义​​:dp[i][j] 表示前 i 个物品在容量 j 时的最大价值。
  • ​​转移方程​​:
    • 不选第 i 个物品:dp[i][j] = dp[i-1][j]
    • 选第 i 个物品:dp[i][j] = dp[i-1][j-w[i]] + v[i]
  • ​​优化​​:
    • 空间压缩到一维数组:dp[j] = max(dp[j], dp[j-w[i]] + v[i]),需​​逆序​​更新 j(从 W 到 w[i])。

​​例题​​:


​​2. 完全背包问题​​

​​问题描述​​:

  • 物品可以选无限次,其他条件同 0-1 背包。

​​解法​​:

  • ​​转移方程​​:
    • dp[i][j] = max(dp[i-1][j], dp[i][j-w[i]] + v[i])(注意 i 不变,表示可重复选)。
  • ​​优化​​:
    • 一维数组​​正序​​更新 j(从 w[i] 到 W)。

​​例题​​:


​​3. 多重背包问题​​

​​问题描述​​:

  • 物品 i 最多选 s[i] 次。

​​解法​​:

  • ​​二进制拆分​​:将 s[i] 拆成 1, 2, 4, ..., 2^k, s[i]-2^k 的组合,转化为 0-1 背包。
  • ​​单调队列优化​​:时间复杂度优化到 O(NW)(较复杂)。

​​例题​​:

  • 经典问题:物品有限次数的背包最大价值。

​​4. 分组背包问题​​

​​问题描述​​:

  • 物品分为若干组,每组只能选一个物品。

​​解法​​:

  • ​​状态转移​​:
    • 对每组枚举所有物品:dp[j] = max(dp[j], dp[j-w[k]] + v[k]),其中 k 是当前组的物品。

​​例题​​:


​​5. 二维费用背包​​

​​问题描述​​:

  • 背包有​​两种容量限制​​(如重量和体积),物品对应两种费用。

​​解法​​:

  • ​​状态扩展​​:dp[i][j][k] 表示前 i 个物品在两种容量 j 和 k 时的最优解。
  • 优化为二维数组:dp[j][k] = max(dp[j][k], dp[j-w1[i]][k-w2[i]] + v[i])。

​​例题​​:


​​6. 背包问题求方案数​​

​​问题描述​​:

  • 求装满背包的​​方案数​​(不要求最优解)。

​​解法​​:

  • ​​状态定义​​:dp[j] 表示容量 j 的方案数。
  • ​​转移方程​​:dp[j] += dp[j-w[i]](初始 dp[0]=1)。

​​例题​​:


​​7. 背包问题求具体方案​​

​​问题描述​​:

  • 要求输出最优解的具体物品选择方案。

​​解法​​:

  • ​​回溯法​​:记录状态转移路径,从 dp[N][W] 倒推物品选择。
  • ​​标记数组​​:在 DP 过程中记录是否选择了当前物品。

​​通用技巧​​

  1. ​​初始化​​:
    • 要求恰好装满:dp[0]=0,其他初始化为 -∞。
    • 不要求装满:dp[0...W]=0。
  2. ​​遍历顺序​​:
    • 0-1 背包:逆序更新容量。
    • 完全背包:正序更新容量。
  3. ​​问题转化​​:将非背包问题(如子集和、字符串匹配)转化为背包模型。

通过掌握这些类型和对应的状态转移方程,可以解决大多数背包 DP 问题。建议从 0-1 背包和完全背包入手,逐步扩展到其他变种。

2.状压dp

状压动态规划(状态压缩 DP)通常用于处理​​状态包含多个维度且每个维度是二元选择​​的问题(如“选/不选”、“存在/不存在”)。其核心是通过​​二进制数​​压缩状态,从而高效地表示和转移状态。以下是常见的状压 DP 类型及解法总结:


​​1. 旅行商问题(TSP)​​

​​问题描述​​:

  • 给定一个带权无向图,求从起点出发经过所有点​​恰好一次​​并回到起点的​​最短路径​​。

​​状态设计​​:

  • dp[mask][u]:表示当前已访问的节点集合为 mask(二进制位表示),且当前位于节点 u 时的最短路径长度。
  • ​​初始化​​:dp[1 << start][start] = 0(起点初始距离为 0)。
  • ​​转移方程​​:
    • 对于所有未访问的节点 v,更新 dp[mask | (1 << v)][v] = min(dp[mask][u] + dist[u][v])。
  • ​​答案​​:dp[(1 << n) - 1][start](所有点都访问过,并回到起点)。

​​优化​​:

  • 预处理两点间最短距离(如 Floyd 算法)。
  • 使用​​记忆化搜索​​减少重复计算。

​​例题​​:


​​2. 棋盘覆盖/放置问题​​

​​问题描述​​:

  • 在棋盘上放置棋子(如车、皇后、多米诺骨牌),要求满足某些限制(如不攻击、不重叠等),求方案数或最大收益。

​​状态设计​​:

  • dp[i][mask]:表示处理到第 i 行时,当前行的状态为 mask(二进制位表示是否放置)。
  • ​​转移方程​​:
    • 枚举上一行的状态 prev_mask,检查是否冲突(如列冲突、对角线冲突)。
    • dp[i][mask] += dp[i-1][prev_mask](合法转移)。

​​例题​​:


​​3. 子集 DP(枚举子集)​​

​​问题描述​​:

  • 对集合的所有子集进行 DP 计算(如子集和、子集覆盖等)。

​​状态设计​​:

  • dp[mask]:表示当前子集 mask 的最优解(如最小操作数、最大价值等)。
  • ​​转移方程​​:
    • 枚举 mask 的所有子集 submask,更新 dp[mask] = min(dp[mask], dp[submask] + cost)。

​​优化​​:

  • ​​枚举子集技巧​​:for (int submask = mask; submask; submask = (submask - 1) & mask)
  • ​​预处理​​:提前计算某些子集的性质(如是否合法)。

​​例题​​:


​​4. 位运算优化 DP​​

​​问题描述​​:

  • 利用位运算(如 AND、OR、XOR)进行状态转移,常用于​​位操作相关的最优化问题​​。

​​状态设计​​:

  • dp[mask]:表示当前位状态 mask 的最优解。
  • ​​转移方程​​:
    • 通过位运算更新状态,如 dp[mask | new_bits] = min(dp[mask] + cost)。

​​例题​​:


​​5. 轮廓线 DP(插头 DP)​​

​​问题描述​​:

  • 处理​​网格图上的路径/连通性问题​​(如哈密顿路径、括号匹配等),适用于​​逐格转移​​的问题。

​​状态设计​​:

  • dp[i][j][mask]:表示处理到 (i, j) 时,轮廓线状态为 mask(记录插头信息)。
  • ​​转移方程​​:
    • 根据当前格是否放置、是否连通等更新 mask。

​​例题​​:


​​通用技巧​​

  1. ​​状态压缩方式​​:
    • 用 int 或 long long 的二进制位表示状态(mask & (1 << i) 检查第 i 位是否选中)。
  2. ​​初始化与边界​​:
    • 初始状态通常为 dp[0][...] = 0 或 dp[1 << start][...] = 0。
  3. ​​优化方法​​:
    • ​​预处理合法状态​​(如 mask 不能有相邻的 1)。
    • ​​滚动数组​​优化空间(如 dp[2][mask])。
  4. ​​时间复杂度​​:
    • 通常为 O(n * 2^n)(适用于 n ≤ 20 的情况)。

​​典型例题​​

类型例题
TSP 问题LeetCode 943. 最短超级串
棋盘放置LeetCode 1349. 最大学生数
子集 DPLeetCode 698. 划分为 k 个子集
位运算 DPLeetCode 847. 访问所有节点的最短路径
插头 DPHDU 1693 Eat the Trees

掌握这些类型后,可以解决大多数状压 DP 问题。建议从 ​​TSP 问题​​ 和 ​​棋盘放置问题​​ 入手,熟悉状态压缩的基本思路。

3.数位dp

数位动态规划(Digit DP)用于解决与​​数字各位数字相关​​的计数或最优化问题,通常涉及​​数字的位数限制、数字性质(如回文、数位和)、区间统计​​等。以下是常见的数位 DP 类型及解法总结:


​​1. 基本数位 DP(统计满足条件的数字个数)​​

​​问题描述​​:

  • 给定区间 [L, R],统计满足某种条件的数字个数(如不含 4、数位递增等)。

​​解法​​:

  1. ​​状态设计​​:
    • dp[pos][state][limit]:
      • pos:当前处理到数字的第 pos 位(从高位到低位)。
      • state:记录前几位的影响(如前缀和、前导零等)。
      • limit:是否受数字上限约束(如 R=123,前两位是 12 时第三位不能超过 3)。
  2. ​​转移方程​​:
    • 枚举当前位的数字 d(0 到 9,受 limit 限制)。
    • 根据 state 更新状态(如数位和、是否出现某数字)。
  3. ​​记忆化搜索​​:
    • 递归实现,记忆化 limit=false 的状态(避免重复计算)。

​​例题​​:


​​2. 数位和问题​​

​​问题描述​​:

  • 统计区间 [L, R] 内数位和满足条件的数字(如和等于 K、是 K 的倍数等)。

​​解法​​:

  • ​​状态扩展​​:在 state 中记录当前数位和 sum。
  • ​​剪枝优化​​:若 sum 超过目标或无法达到目标,提前终止。

​​例题​​:


​​3. 数字性质问题(回文、单调性等)​​

​​问题描述​​:

  • 统计满足特定性质的数字(如回文数、数位递增/递减等)。

​​解法​​:

  • ​​状态设计​​:
    • 回文数:记录前一半数字,检查是否对称。
    • 单调递增:记录前一位数字,确保当前位不小于前一位。

​​例题​​:


​​4. 数字与模数问题​​

​​问题描述​​:

  • 统计数字模 K 等于特定值的数字个数(如 %K=0)。

​​解法​​:

  • ​​状态扩展​​:在 state 中记录当前数字模 K 的值 mod。
  • ​​转移方程​​:
    • new_mod = (mod * 10 + d) % K,更新状态。

​​例题​​:


​​5. 数字转换问题(二进制、特殊进制)​​

​​问题描述​​:

  • 处理非十进制数字(如二进制、三进制)的统计问题。

​​解法​​:

  • ​​调整数位枚举范围​​(如二进制枚举 0 或 1)。
  • ​​状态设计​​与十进制类似,但需注意进制转换。

​​例题​​:


​​通用技巧​​

  1. ​​问题转化​​:
    • 将 [L, R] 的问题转化为 [0, R] - [0, L-1]。
  2. ​​状态设计原则​​:
    • 必须包含​​当前位 pos​​ 和​​约束状态 limit​​。
    • 根据问题添加额外状态(如 sum、mod、前导零 lead)。
  3. ​​记忆化优化​​:
    • 仅记忆化 limit=false 的状态(limit=true 的状态只会计算一次)。
  4. ​​边界处理​​:
    • 注意 L=0 或 R=1e18 等特殊情况。

​​模板代码(记忆化搜索)​​

def digit_dp(num):
    s = str(num)
    n = len(s)
    
    @cache
    def dfs(pos, state, limit, lead):
        if pos == n:
            return 1 if state_valid(state) else 0  # 根据问题调整
        res = 0
        up = int(s[pos]) if limit else 9
        for d in range(0, up + 1):
            new_limit = limit and (d == up)
            new_lead = lead and (d == 0)
            if new_lead:
                res += dfs(pos + 1, state, new_limit, new_lead)
            else:
                new_state = update_state(state, d)  # 根据问题更新状态
                res += dfs(pos + 1, new_state, new_limit, new_lead)
        return res
    
    return dfs(0, init_state, True, True)

​​典型例题​​

类型例题
基本计数LeetCode 233. 数字 1 的个数
数位和SPOJ SUMTRIAN
回文数LeetCode 1067. 数字计数
模数问题Codeforces 55D. Beautiful numbers
二进制 DPLeetCode 600. 不含连续 1 的整数

掌握这些类型后,可以解决大多数数位 DP 问题。建议从​​基本计数问题​​(如统计不含 4 的数字)入手,逐步扩展到复杂状态设计(如模数、数位和)。

4.区间dp

区间动态规划(Interval DP)是一种用于解决​​区间划分、合并、最优解​​问题的动态规划方法,其核心思想是通过枚举区间的分割点,逐步求解更大区间的最优解。以下是常见的区间 DP 类型及解法总结:


​​1. 区间最值/计数问题​​

​​问题描述​​:

  • 给定一个序列(如数组、字符串),求满足条件的区间的最值(如最大值、最小值)或方案数。

​​解法​​:

  • ​​状态定义​​:
    • dp[i][j]:表示区间 [i, j] 的最优解或方案数。
  • ​​转移方程​​:
    • 枚举分割点 k(i ≤ k < j),将区间 [i, j] 拆分为 [i, k] 和 [k+1, j],合并结果。
    • 例如:dp[i][j] = max(dp[i][k] + dp[k+1][j] + cost(i, j, k))。

​​例题​​:


​​2. 区间合并问题(合并代价最小化)​​

​​问题描述​​:

  • 将多个区间合并为一个,每次合并相邻区间需支付一定代价,求最小总代价。

​​解法​​:

  • ​​状态定义​​:
    • dp[i][j]:表示合并区间 [i, j] 的最小代价。
  • ​​转移方程​​:
    • dp[i][j] = min(dp[i][k] + dp[k+1][j] + sum(i, j)),其中 sum(i, j) 是区间 [i, j] 的权重和。

​​例题​​:


​​3. 区间划分问题(分割为若干子区间)​​

​​问题描述​​:

  • 将一个序列划分为若干子区间,每个子区间需满足特定条件(如和不超过 K),求最小划分数或最大收益。

​​解法​​:

  • ​​状态定义​​:
    • dp[i]:表示前 i 个元素的最优解(通常一维即可)。
  • ​​转移方程​​:
    • dp[i] = min(dp[j] + cost(j+1, i)),其中 j < i 且 [j+1, i] 满足条件。

​​例题​​:


​​4. 区间匹配问题(括号、回文等)​​

​​问题描述​​:

  • 处理括号匹配、回文子序列等需要对称性的区间问题。

​​解法​​:

  • ​​状态定义​​:
    • dp[i][j]:表示区间 [i, j] 是否匹配或最长匹配长度。
  • ​​转移方程​​:
    • 若 s[i] 和 s[j] 匹配(如括号或相同字符),则 dp[i][j] = dp[i+1][j-1] + 2。
    • 否则,dp[i][j] = max(dp[i+1][j], dp[i][j-1])。

​​例题​​:


​​5. 环形区间 DP(破环成链)​​

​​问题描述​​:

  • 当序列是环形(如首尾相连)时,需特殊处理。

​​解法​​:

  • ​​破环成链​​:将原数组复制一份接在后面(nums + nums),然后对 2n 长度的序列做区间 DP。
  • ​​最终答案​​:枚举所有可能的起点 i,取 dp[i][i+n-1] 的最优解。

​​例题​​:


​​通用技巧​​

  1. ​​遍历顺序​​:
    • 区间 DP 通常按​​区间长度从小到大​​枚举(先算小区间,再算大区间)。
  2. ​​初始化​​:
    • 单个元素的区间 dp[i][i] 通常有初始值(如回文长度为 1,合并代价为 0)。
  3. ​​时间复杂度优化​​:
    • 四边形不等式优化(将 O(n^3) 优化到 O(n^2))。
  4. ​​空间优化​​:
    • 滚动数组(如 dp[2][n])或对角线遍历(减少空间占用)。

​​模板代码(区间 DP 框架)​​

def interval_dp(s):
    n = len(s)
    dp = [[0] * n for _ in range(n)]
    
    # 初始化单个字符或小区间
    for i in range(n):
        dp[i][i] = initial_value
    
    # 枚举区间长度
    for length in range(2, n + 1):
        for i in range(n - length + 1):
            j = i + length - 1
            # 枚举分割点
            for k in range(i, j):
                dp[i][j] = update(dp[i][j], dp[i][k] + dp[k+1][j] + cost(i, j, k))
    
    return dp[0][n-1]

​​典型例题​​

类型例题
区间最值LeetCode 312. 戳气球
区间合并LeetCode 1000. 合并石头的最低成本
区间划分LeetCode 132. 分割回文串 II
区间匹配LeetCode 5. 最长回文子串
环形 DP洛谷 P1880 石子合并

掌握这些类型后,可以解决大多数区间 DP 问题。建议从​​石子合并问题​​和​​戳气球问题​​入手,熟悉区间 DP 的基本思路。

5.树型dp

树型动态规划(Tree DP)是一种基于树结构的动态规划方法,常用于处理​​树上的最优解、计数、路径统计​​等问题。以下是常见的树型 DP 类型及解法总结:


​​1. 基础树型 DP(子树统计)​​

​​问题描述​​:

  • 统计树中每个子树的性质(如节点数、最大值、和等)。

​​解法​​:

  • ​​状态定义​​:
    • dp[u]:表示以节点 u 为根的子树的最优解或统计值。
  • ​​转移方程​​:
    • 递归遍历子树,合并子节点的结果:dp[u] = combine(dp[v1], dp[v2], ...)。
  • ​​遍历方式​​:
    • 后序遍历(先处理子节点,再处理父节点)。

​​例题​​:


​​2. 树上背包问题(分组依赖)​​

​​问题描述​​:

  • 在树中选择节点(如保留/删除),满足某些依赖关系(如选子节点必须选父节点),求最大/最小价值。

​​解法​​:

  • ​​状态定义​​:
    • dp[u][k]:表示以 u 为根的子树中选择 k 个节点的最优解。
  • ​​转移方程​​:
    • 类似分组背包,枚举子节点的分配数量:
      for v in children[u]:
          for j in range(k, 0, -1):  # 逆序避免重复计数
              for t in range(1, j):  # 分配给子节点 v 的容量
                  dp[u][j] = max(dp[u][j], dp[u][j-t] + dp[v][t])
  • ​​优化​​:
    • 如果树是二叉树,可以用左右子树分开处理。

​​例题​​:


​​3. 换根 DP(二次扫描)​​

​​问题描述​​:

  • 对每个节点作为根时,求整棵树的性质(如节点到其他节点的距离和、最大值等)。

​​解法​​:

  1. ​​第一次 DFS​​:计算以某个根(如 u=0)为基准的子树信息(如 dp[u])。
  2. ​​第二次 DFS​​:通过父节点的信息推导其他节点的值:
    • dp[v] = dp[u] - contribution(v) + new_contribution(v)。

​​例题​​:


​​4. 树的路径问题(最长/最短路径)​​

​​问题描述​​:

  • 求树中满足条件的路径(如最长路径、路径和等于 K 的路径数等)。

​​解法​​:

  • ​​状态定义​​:
    • dp[u][0/1]:记录以 u 为根的子树的最长路径(可能需要分是否拐弯)。
  • ​​转移方程​​:
    • 最长路径可能由两条子路径拼接而成:
      dp[u][0] = max(dp[v][0] + w)  # 不拐弯的路径
      dp[u][1] = max(dp[u][0], dp[v1][0] + dp[v2][0] + w1 + w2)  # 拐弯路径

​​例题​​:


​​5. 树的染色问题(相邻节点限制)​​

​​问题描述​​:

  • 给树节点染色,相邻节点有颜色限制(如不同色),求方案数或最小成本。

​​解法​​:

  • ​​状态定义​​:
    • dp[u][c]:表示节点 u 染颜色 c 时的方案数或成本。
  • ​​转移方程​​:
    • 枚举子节点的颜色(不与父节点冲突):
      for v in children[u]:
          for c_child in colors:
              if c_child != c:
                  dp[u][c] += dp[v][c_child]

​​例题​​:


​​通用技巧​​

  1. ​​遍历顺序​​:
    • 通常用 ​​DFS 后序遍历​​(先处理子树,再处理父节点)。
  2. ​​状态设计​​:
    • 根据问题决定是否需要记录父节点状态(如 dp[u][c][parent_c])。
  3. ​​空间优化​​:
    • 如果子节点状态可复用,可以用滚动数组(如 dp[2][...])。
  4. ​​边界处理​​:
    • 叶子节点的初始化(如 dp[leaf][...] = base_value)。

​​模板代码(树型 DP 框架)​​

def tree_dp(root):
    # 初始化 DP 表
    dp = defaultdict(dict)
    
    def dfs(u, parent):
        # 初始化当前节点的状态
        dp[u][...] = initial_value
        
        for v in tree[u]:
            if v == parent:
                continue
            dfs(v, u)  # 递归处理子节点
            # 合并子节点的状态
            dp[u][...] = combine(dp[u][...], dp[v][...])
    
    dfs(root, -1)
    return dp[root][...]

​​典型例题​​

类型例题
子树统计LeetCode 543. 二叉树的直径
树上背包LeetCode 337. 打家劫舍 III
换根 DPLeetCode 834. 树中距离之和
路径问题LeetCode 687. 最长同值路径
染色问题LeetCode 968. 监控二叉树

掌握这些类型后,可以解决大多数树型 DP 问题。建议从​​基础子树统计​​和​​树上背包问题​​入手,逐步扩展到换根 DP 和路径问题。

常见方法

在动态规划(DP)问题中,除了掌握各类问题的状态设计和转移方程外,​​通用优化技巧和解题策略​​同样至关重要。以下是结合前文所有讨论的 ​​3 种常见方法及其适用场景​​的总结,帮助你在实战中快速识别问题并优化解法。


​​1. 优化枚举:减少无效状态或转移​​

​​核心思想​​:通过数学性质、贪心策略或问题约束,减少需要枚举的状态或决策,降低时间复杂度。

​​适用场景及技巧​​:
  • ​​背包问题​​:
    • ​​完全背包​​:正序枚举容量(j 从 w[i] 到 W),避免重复计算的无效状态。
    • ​​多重背包​​:二进制拆分将 s[i] 次物品拆分为 1, 2, 4,... 的组合,转化为 0-1 背包,减少枚举次数。
  • ​​区间 DP​​:
    • ​​四边形不等式优化​​:将分割点 k 的枚举范围从 [i, j) 缩小到 [k_opt[i][j-1], k_opt[i+1][j]],时间复杂度从 O(n^3) 降至 O(n^2)。
    • ​​例题​​:石子合并问题。
  • ​​树型 DP​​:
    • ​​树上背包​​:逆序枚举容量(类似 0-1 背包),避免子树状态重复计算。
    • ​​例题​​:洛谷 P2014 选课。
​​经典案例​​:
  • ​​LeetCode 322. 零钱兑换​​:完全背包中,正序枚举金额 amount 可确保硬币无限使用。

​​2. 根据数据量猜解法:从输入规模反推算法​​

​​核心思想​​:通过题目给出的数据范围,快速判断可能的 DP 类型和优化方向。

​​常见数据范围与解法​​:
数据范围 (n)可能的 DP 类型优化思路
n ≤ 20状压 DP二进制状态压缩
n ≤ 100区间 DP / 二维背包O(n^3) 或 O(n^2) 优化
n ≤ 1000线性 DP / 树型 DP滚动数组优化空间
n ≤ 1e5换根 DP / 贪心 + DP线性扫描或数学性质
​​实战技巧​​:

​​3. 根据 DP 表反推具体方案:回溯或记录路径​​

​​核心思想​​:在 DP 求解最优值后,通过反向追踪状态转移路径,还原具体方案(如选哪些物品、如何分割区间等)。

​​常见方法​​:
  1. ​​记录转移来源​​:
    • 在 DP 表中额外维护 from[i][j],记录状态 (i,j) 的最优转移来源。
    • ​​例题​​:LeetCode 1143. 最长公共子序列,通过 from 数组回溯 LCS 字符串。
  2. ​​贪心回溯​​:
    • 根据 DP 值的单调性反向推导(如从终点倒推起点)。
    • ​​例题​​:LeetCode 45. 跳跃游戏 II(记录每一步的最远跳跃位置)。
  3. ​​分步重构​​:
    • 适用于背包问题,通过 dp[j] 和 dp[j-w[i]] 的差值判断是否选了物品 i。
    • ​​例题​​:0-1 背包输出具体方案。
​​代码模板(以 0-1 背包为例)​​:
# 假设 dp[j] 表示容量 j 的最大价值,from[j] 记录是否选了物品 i
for i in range(n, 0, -1):
    if j >= w[i] and dp[j] == dp[j - w[i]] + v[i]:
        print(f"选物品 {i}")
        j -= w[i]

​​综合应用场景​​

  1. ​​背包问题 + 输出方案​​:
    • 先跑标准 DP,再逆序检查物品是否被选。
  2. ​​区间 DP + 构造分割点​​:
    • 记录分割点 k,递归输出左右区间(如矩阵连乘问题)。
  3. ​​树型 DP + 换根求全局解​​:

​​总结:方法选择与优先级​​

  1. ​​先看数据范围​​:确定 DP 类型(状压、区间、树型等)。
  2. ​​优化枚举​​:优先考虑数学性质或问题约束(如单调性、贪心剪枝)。
  3. ​​方案还原​​:若需输出路径,设计 DP 表时同步记录转移来源。

通过结合这些方法,可以高效解决大多数 DP 问题,并在竞赛或面试中快速定位优化方向。

Ollama内网离线部署大模型实战:从模型文件到私有化服务 本文详细介绍了如何在内网离线环境下,利用Ollama部署私有化大模型服务。内容涵盖从获取GGUF格式模型文件、手动安装Ollama、编写核心Modelfile配置文件,到创建运行模型、性能调优及安全加固的全流程实战指南,旨在帮助企业安全、可控地构建内部AI能力。 阅读详情

相关推荐

Cocos Creator Android打包:NDK版本选择与配置全攻略

在Android原生开发中,NDK(Native Development Kit)是编译C/C++代码的核心工具链,它决定了代码如何被编译为不同CPU架构(如ARMv7、ARM64)的动态库。其工作原理在于提供了一套完整的交叉编译环境,将高级语言代码转化为设备可执行的二进制指令。这一技术对于游戏引擎和性能敏感型应用至关重要,因为它能直接调用硬件资源,提升运行效率。在实际工程中,NDK版本必须与Android Gradle插件(AGP)版本、项目依赖的C++标准库精密匹配,否则会导致编译失败或运行时崩溃。特别

weixin_33725270的博客 491

动态规划算法及其使用

动态规划算法介绍以及应用(包含经典例题:找出连续子序列的最大和)

weixin_75116169的博客 1075

滑模控制算法——基本原理(附MATLAB程序)

选择一个滑模面和是设计参数。是滑模面函数,通常我们希望系统状态在的面上滑动。我们以一个简单的二阶系统作为示例,其状态空间方程为:是系统矩阵是输入矩阵是输出矩阵假设我们的系统如下:滑模控制算法的核心在于设计合适的滑模面和控制律,并分析系统在滑模面上的稳定性。通过上述公式和步骤,可以有效地设计滑模控制器,以处理具有非线性和不确定性的动态系统。总的来说,滑模控制算法因其鲁棒性和灵活性在处理复杂控制问题中表现出色,但在实际应用中需要精心设计以克服抖振现象和其他潜在问题。BuxuABDC。

qq_35623594的博客 2万+

算法与数据结构——动态规划(java)(b站左程云课程笔记总结)

算法与数据结构——动态规划(Java)

Borslav的博客 1058

GEO优化内容营销策略:从规划到落地的全流程指南

GEO内容营销是通过地域化内容提升本地搜索排名、增加流量和转化率的策略。核心包括:构建本地用户画像,挖掘需求关键词;策划本地文化融合、案例研究等多样化内容;通过网站、社交媒体、本地KOL等渠道精准分发;优化本地关键词和视觉元素;监测数据并动态调整。案例显示,咖啡馆、工业企业和服装品牌通过本地化短视频、KOL合作及社群运营,显著提升了搜索排名、客流量和销售额。关键原则是内容本地化、精准分发、KOL合作、激励用户生成内容及数据驱动优化,实现高效本地市场渗透。

解锁AI流量新红利!🚀 GEO优化(生成引擎优化)助您掌控AI搜索,让AI成为您的品牌最佳代言人! 1330

simulink建模仿真实例1000例--第375例:基于Simulink进行复杂交通流的仿真建模

特点多车互动:模拟多个车辆之间的相互作用。动态变化:交通流量随时间和事件(如事故、天气变化)而变化。多样化驾驶行为:考虑不同的驾驶员行为模式(激进型、保守型等)。挑战准确性:准确地模拟各种因素对交通流的影响。实时性:处理大量车辆数据,并迅速做出反应。可扩展性:支持从简单场景到复杂城市交通网络的扩展。首先明确要模拟的任务内容。模拟不同类型的车辆(轿车、卡车、公交车等)。考虑不同驾驶员的行为模式。分析交通拥堵、事故对交通流的影响。

xiaoheshang_123的博客 203

动态规划及实例

01背包问题:一个容量为C的背包,n个物品,他们的重量分别是w[i],价值分别是v[i],怎样选择装入的物品使价值最大?分析:面对每个物品,我们只有选择拿取或者不拿两种选择,不能选择装入某物品的一部分,也不能装入同一物品多次。所以声明一个 大小为  m[n][c] 的二维数组,m[ i ][ j ] 表示 在面对第 i 件物品,且背包容量为  j 时所能获得的最大价值 ,那么我们可以很容易分析得出...

kobe_jr的博客 664

Qt c++ llama.cpp 运行 LLaMA-3-8B 中文对话机器人(多模态输入)

助手:

寒寒豆豆软件公司的博客 222

2018-9-21 哔哩哔哩笔试编程题

22娘和33娘接到了小电视君的扭蛋任务: 一共有两台扭蛋机,编号分别为扭蛋机2号和扭蛋机3号,22娘使用扭蛋机2号,33娘使用扭蛋机3号。 扭蛋机都不需要投币,但有一项特殊能力: 扭蛋机2号:如果塞x(x范围为&gt;=0正整数)个扭蛋进去,然后就可以扭到2x+1个 扭蛋机3号:如果塞x(x范围为&gt;=0正整数)个扭蛋进去,然后就可以扭到2x+2个 22娘和33娘手中没有扭蛋,需要你...

sinat_34319731的博客 3115

Qt Quick/QML入门到精通_专栏demo对应文章目录(目前50个左右demo/长期更新)

自定义图标下拉列表框的效果如下所示:自定义三态按钮的效果如下所示:传送门:53.qt quick-自定义三态按钮、自定义图标下拉列表框_诺谦的博客-优快云博客传送门:59.qt quick-qml调色板(颜色取色器)实现、自定义QQuickStyle样式_诺谦的博客-优快云博客63.qt quick-QML侧边滑动栏(不需要任何图片资源,支持自定义左右方向和大小)_诺谦的博客-优快云博客_qt实现好看侧边栏。...

抵抗时间扭曲,坚持做一件有意义的事 2万+

什么是 AI Agent?它和直接调用大模型 API 有什么区别?

什么是 AI Agent?它和直接调用大模型 API 有什么区别?

我哎GIS博客 6486

class088 动态规划专题总结与预告【算法】

class088 动态规划专题总结与预告【算法】

小心星的博客 574

性能调优秘籍——让OpenClaw运行速度提升10倍(2026优化版)

OpenClaw性能优化核心摘要 本文详细介绍了提升OpenClaw系统性能的关键技术,包括: 使用性能分析器进行代码级性能诊断 内存优化与高效缓存策略 并发处理优化技术 数据库查询性能提升方法 重点内容包括: 全面的性能分析工具实现(计时、内存监控、错误跟踪) 自动生成性能报告和优化建议 操作耗时与内存占用分析算法 关键性能指标(平均耗时、内存增长)计算 通过系统性的性能分析优化,可使OpenClaw运行速度提升10倍以上。

博客 903

黑群晖 RR引导 完整教程:5步完成安装引导盘并学会自救

RR引导(Redpill Recovery)就是一张能装系统也能救场的引导盘:写进 U 盘,任意 x86/x64 机器即可安装黑群晖 DSM,装砸了同一张盘还能进恢复环境找回数据。本文适合准备自建 NAS 的新手,当前版本 26.7.4,按下面 5 个步骤走即可。 ## 🧩 ① 下载镜像前,先做 4 项硬件自查 大部分安装失败源于硬件不达标,对照下表逐项打勾再动手: | 检查项 | 要求

gitblog_00052的博客 1086

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

ShowDoc 与 PHP 生态中的 sebastian/diff:从 Composer 安装到 Differ/Parser 的完整实践指南

sebastian/diff 是 PHP 社区中最知名的通用 Diff 组件之一,它从 PHPUnit 中独立拆分而来,专门负责"两个字符串/数组之间的差异计算与表示"。本文以 ShowDoc 仓库中实际锁定的 4.0.6 版本(见 [composer.lock](https://link.gitcode.com/i/b74d4a4de68130b17a0c2a385c3e8a5b))为主体,完整

gitblog_00024的博客 391

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

视频隐私脱敏工程化:CSRT/KCF追踪算法在本地推理中的实现与边界

在短视频内容合规审查中,动态遮挡(Dynamic Masking)是高频且高耗时的环节。传统手动关键帧打码不仅效率低下,且在目标快速移动或遮挡时极易出现遮罩漂移。本文从计算机视觉工程角度,解析基于CSRT与KCF算法的目标追踪方案在视频隐私处理中的技术路径、性能权衡及落地实践。

Mysssone的博客 295

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

【算法拾光】链表 哈希表|K个一组翻转链表 + 两数之和

K个一组翻转链表需分段反转,借助哑节点与前驱指针维护连接,避免断链;两数之和利用哈希表实现O(1)查找,空间换时间,高效定位目标组合。前者考察链表指针操作,后者聚焦映射关系构建,均为面试高频题。

wuqingyigexiaoba的博客 327

把 KV Cache 压到 FP4:BitDecoding-FP4 的算法-系统协同设计复盘

长上下文推理卡在KV Cache的显存与带宽上,而现有FP4方案要么精度崩、要么速度废。这篇文章复盘BitDecoding-FP4如何用三个算法补丁加一个系统重构,在B300上把服务吞吐提升2.4–3.0倍,精度几乎不掉。

helloagent的博客 308

排序算法深度笔记(下):非比较类排序的线性突破与工程边界

排序算法时间复杂度额外空间复杂度稳定性是否基于比较选择排序O(N^2)(O(1))不稳定是冒泡排序O(N^2)(O(1))稳定是插入排序O(N^2)(O(1))稳定是归并排序O(N\log N)(O(N))稳定是快速排序O(N\log N)O(log N)不稳定是堆排序O(N\log N)O(1)不稳定是计数排序(O(N + M))O(M))稳定否基数排序O(N)稳定否桶排序平均O(N) / 最坏O(N^2)

2401_87069538的博客 601

优选算法:分治-归并排序

交易逆序对的总数

Wangshijie1015的博客 144

算法日常・每日刷题--<动态规划>42

本题为“不相交的线”,与最长公共子序列问题相似。使用动态规划求解:定义 dp[i][j] 表示 nums1[0:i] 与 nums2[0:j] 的最大不相交连线数。若元素相等,则继承左上值加1;否则取上方或左方最大值。最终返回 dp[m][n],即两数组的最大不相交连线数。时间复杂度 O(mn),空间复杂度 O(mn)。

2503_92261635的博客 23

408 算法 06|堆与 Top-K:最小的 10 个数,到底该用数组还是堆

2022(42) 找 100 万个数里最小的 10 个,官方给了"10 元素有序数组"和"大根堆"两种答案。我把两种都实现并数了比较次数:k=10 时两者几乎打平,k=10000 时堆少 111 倍。另附 2021(42) 计数排序的比较次数与稳定性实测。

m0_56226131的博客 359

408 算法 04|主元素与三元组最小距离:两种“扫描时做决定“的思路

2013(41) 的主元素用摩尔投票一遍选出候选,但真正的考点是"选完必须验票";2020(41) 的三元组最小距离,三指针每次只挪最小的那个,靠的是 D=2×(max−min) 这个恒等式。两套完整 C 代码加真实输出,附官方评分档位表。

m0_56226131的博客 417

牛客周赛165C题(前后缀性质,递推,排序,枚举)

题目大意:给定长度为 `n` 的数组,选择 `k ∈ [1,n]`,将前 `k` 个元素整体翻转,后面的元素不变。要求操作后的整个数组非递减,统计满足条件的 `k` 的数量。题目链接:https://ac.nowcoder.com/acm/contest/141721/C。时间复杂度:O(nlogn)

2501_94316951的博客 173

【MPC 算法】Python 实战:模型预测控制(MPC)算法原理与代码实现

本文介绍模型预测控制(MPC)原理与Python实现。MPC通过滚动优化预测未来状态,结合系统模型、目标函数与约束条件,动态生成最优控制序列。以一阶温度系统为例,使用CVXPY构建凸优化问题,实现加热功率的智能调控。代码包含模型预测、目标函数设计(含温度跟踪与控制平滑惩罚)、输入约束及滚动更新机制。仿真结果表明,MPC能快速稳定温度至目标值60℃,且控制动作平滑合理,体现其在复杂系统中的优越性。

赖德发的博客 39

算法61,判定是否为字符重排

两字符串能互为重排 ⇔。

sheshibenniaodan的博客 221
上一篇: 算法讲解087【必备】动态规划中根据数据量猜解法的技巧
下一篇: 算法讲解005【入门】对数器-验证的重要手段
chbmvdd
博客等级 码龄2年 163粉丝 39原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值