引入
动态规划简介
- 动态规划
- 线性 :状态定义与题设线性相关
- 将原问题分解成若干子问题
- 设计状态:状态是当前问题所在的局面
- 满足性质:无后效性,最优子结构
- 转移:状态之间的关系,用转移方程描述
- 动态规划(递推) 和 记忆化搜索(递归) 时间复杂度相同
- 记忆化搜索可以规避转移顺序,且方便处理边界情况
- 记忆化搜索效率较低,且难以优化、调试
例子:最长上升子序列
- 状态:以第 个元素结尾的最长上升子序列()的长度,用 表示
- 转移:最长上升子序列一定是接在某个上升子序列尾部形成的
- 刻画整体的转移路径:
- 把每一个状态视作一个节点,把状态转移视作一条有向边,串联起来构成有向无环图()
- 所以根据拓扑序对图进行遍历来求解问题(拓扑排序)
- 动态规划遍历顺序称为阶段
动态规划三要素
- 状态(描述问题局面)
- 转移(状态之间的数学关系)
- 阶段(计算状态的顺序)
B3635 硬币问题
- 题意简述:用 元的硬币凑出 元,最少需要多少硬币?
- 状态: 表示凑出 元所需要的最少硬币数量
- 转移:
- 解释:凑出 元有三种局面
- 凑出 元,拿出一个 元硬币
- 凑出 元,拿出一个 元硬币
- 凑出 元,拿出一个 元硬币
- 取三个当中的最小值
一维线性
- 使用 求出 LIS 和 LDS,枚举中间最高的那个人
优化最长上升子序列
- 第一种枚举时间复杂度 ,但存在大量重复枚举
- 记录以前选的所有数的下标,对于元素
- 如果 小于最大值,那么替换掉第一个大于它的数
- 如果 大于最大值,那么插入到最后
- 优化后时间复杂度
二维线性
- 状态:设 表示到了位置 时可以得到的最大数字和
- 转移:
例子:最长公共子序列
优化前
- 状态: 表示第一个排列前 个元素和第二个排列前 个元素,最长公共子序列的长度
- 转移:
优化后
- 重新标号:把 用字母标成单调递增数列,再用这样的标号标记 ,求 的最长上升序列
- 表示走了 步,甲走到 ,乙走到
- 把 step 一维省略:变为四维 dp
-
背包
01 背包(一个物品只能选择一次)
- 表示重量, 表示价值, 表示物品个数, 表示背包容量, 表示 数组
- 注意:第二层循环必须从大到小枚举!
for (int i = 1; i <= N; i ++) {
for (int j = W; j >= w[i]; j --)
f[j] = max(f[j], f[j - w[i]] + v[i]);
}
完全背包(每个物品可以无限次选择)
for (int i = 1; i <= N; i ++) {
for (int j = w[i]; j <= W; j ++)
f[j] = max(f[j], f[j - w[i]] + v[i]);
}
多重背包(每个物品可以选择 次)
- 第一种:转换成 01 背包
- 第二种(二进制优化):把每个 拆成二进制表示,即,变成了 ,再进行 01 背包
- 由于总时间有限,把完全背包的无限次转化为 次
- 01 背包就是多重背包中的
- 物品被划分成 组,每一组内只能选择最多 个物品
- 组内先枚举物品重量,再内聚物品编号
- 共三维:组、重量、编号
- 每一种主件的四种情况
- 只购买主件
- 购买主件,附件 A
- 购买主件,附件 B
- 购买主件,附件 AB
区间
区间 介绍
- 在区间上做动态规划,求在区间上的最优解
- 区间 的状态通常包括区间信息
- 子区间信息满足区间加法
- 分治思想:大区间信息由小区间获得
对环形问题的处理
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现