Skip to content
← 返回算法列表
问题与数据结构

动态规划 ​

本页按场景选择方法;通过各条目链接阅读完整推演、源码和输出。

前置是循环、切片和递归;每个 package main 在独立模块中保存为 main.go(Go 1.22+),本轮未执行。n、容量、数量都非负,背包数组必须等长、重量为正,跳跃步数非负。矩阵链维度为正;TSP 输入是小规模完整费用矩阵,路径费用小于 inf 且加法不溢出,不把不可达哨兵直接当费用。

要解决的问题可选方法选择前确认
递推只依赖前两个结果,或只判断最远可达斐波那契、爬楼梯、跳跃游戏前两者可滚动保存状态;跳跃游戏示例是贪心,仅判断能否到达。
每件物品限一次、可无限次或有数量上限0/1 背包、完全背包、多重背包0/1 容量倒序,完全背包正序且权重须为正,多重背包拆成二进制组。
比较序列顺序与连续片段LIS、LCS、最长公共子串LIS 示例只返回长度;LCS 允许跳过字符,公共子串要求连续且失配归零。
计算修改字符串的最小代价或寻找回文编辑距离或最长回文子串编辑距离示例按字节计操作;回文示例用 rune 中心扩展,时间 O(n²)。
答案依赖连续子区间或孩子状态区间 DP或树形 DP矩阵链按区间长度计算,时间 O(n³);树形示例假设输入确为一棵树。
精确求解少量城市的最短回路状态压缩 DP状态数 2ⁿ,示例时间 O(n²2ⁿ),不适合大规模路线规划。

从滚动递推到最远可达判断 ​

斐波那契动态规划 ​

正文、推演与完整示例:斐波那契动态规划。

朴素递归会重复计算大量相同子问题;滚动变量 a、b 分别保存前两个结果。每轮前进一项,既不需要递归栈也不需要完整数组。 时间 O(n)、额外空间 O(1)。

Go 的 int 会在数值过大时溢出,超大 n 应改用 math/big。

爬楼梯 ​

正文、推演与完整示例:爬楼梯。

这里每次允许走 1 或 2 阶;允许更多步长时递推式也要随之改变。

跳跃游戏 ​

正文、推演与完整示例:跳跃游戏。

这个贪心只判断能否到达,不计算最少跳跃次数。

按物品可用次数选择背包转移 ​

0/1 背包 ​

正文、推演与完整示例:0/1 背包。

dp[c] 表示容量不超过 c 时能得到的最大价值。处理某件物品时从大容量倒序更新,确保右侧读取的是“未用过当前物品”的旧状态。 每件物品最多选一次。

若正序更新,同一件物品可能在同轮被选多次,题意就变成完全背包。

完全背包 ​

正文、推演与完整示例:完全背包。

权重必须为正;零重量且正价值的物品会让最优值无界。

多重背包 ​

正文、推演与完整示例:多重背包。

把物品数量按 1、2、4、…及余量拆成若干组,每组视为一件 0/1 物品。例如数量 5 拆为 1、2、2,可以组合出 0 到 5 的取用数量。这比逐件展开减少处理次数,但仍需保证组内重量和价值乘法不溢出。

求递增序列、公共片段、编辑距离与回文 ​

最长递增子序列 ​

正文、推演与完整示例:最长递增子序列。

tails[len−1] 保存某个长度的递增子序列可能达到的最小尾值。遇到新元素时,用二分找到第一个不小于它的位置并替换;这不会直接保存答案序列,但保留了未来延长的最好机会。 时间 O(n log n)。

要重建序列,还需记录前驱下标。

最长公共子序列 ​

正文、推演与完整示例:最长公共子序列。

dp[i][j] 考虑两个字符串各自的前 i、j 个字符。末尾字符相同就接在更短前缀的答案后;不同则分别跳过一侧末尾,取较大者。 时间 O(nm)。

子序列允许不连续,示例按 UTF-8 字节比较;按字符语义处理中文时应改成 rune。

最长公共子串 ​

正文、推演与完整示例:最长公共子串。

示例按字节比较,处理 Unicode 字符时可先转成 rune 切片。

编辑距离 ​

正文、推演与完整示例:编辑距离。

dp[i][j] 是把第一个前缀改成第二个前缀所需的最少操作数。最后字符相同时沿对角线继承,否则在插入、删除和替换三种操作中取最小代价。 二维 DP 时间 O(nm)。

示例按字节计操作;中文字符级编辑距离应把输入转成 rune。

最长回文子串 ​

正文、推演与完整示例:最长回文子串。

回文中心既可能是一个字符,也可能位于两个字符之间,因此每个位置尝试奇数和偶数两类中心。两侧字符相等时继续扩张,并记录最长范围。 中心扩展时间 O(n²),转为 rune 切片占 O(n) 空间。

示例先转成 rune,能正确处理多字节字符;超大输入可改用线性时间的 Manacher 算法。

按区间或树结构汇总子问题 ​

区间 DP ​

正文、推演与完整示例:区间 DP。

矩阵链的子问题由连续区间 [l,r] 决定;只有更短区间已经算出,才能枚举切点 k 并组合左右最优代价。按区间长度递增填表满足这一依赖顺序。 目标是矩阵链最少乘法次数。

矩阵维度数组要满足相邻矩阵可以相乘,算法时间 O(n³)。

树形 DP ​

正文、推演与完整示例:树形 DP。

对每个树节点分别计算“选当前节点”和“不选当前节点”两种状态。选中时孩子都不能选;不选时每个孩子可独立取较优状态。后序递归保证孩子先算好,最后取根的两种状态较大值。 示例求最大独立集权重。

示例假设输入是一棵树。

把已访问集合编码成状态 ​

状态压缩 DP ​

正文、推演与完整示例:状态压缩 DP。

mask 的每一位表示某个城市是否已经访问,dp[mask][v] 表示访问该集合并停在 v 的最短长度。对每个未访问城市尝试转移,最后加上回到起点的费用。 示例求小规模旅行商最短回路,时间 O(n²2ⁿ)。

状态数为 2ⁿ,适合 n 较小的精确求解,不适合大规模路线规划。

静态自检 ​

把 0/1 背包改成容量正序,单件重量 2、价值 3、容量 4 会得到多少?提示:看 dp[2] 是本轮还是上轮。

参考解法

会得到 6,而正确的限用一次结果为 3;本轮更新的 dp[2] 被再次使用,相当于允许重复选择。完全背包才使用这种正序转移。

继续阅读