算法基础课——动态规划(一)笔记
背包问题
- 动态规划重点:状态表示 & 状态转移
- 定义:有编号为 $1$ ~ $N$ 的 $N$ 个物品和一个体积为 $V$ 的背包,对于每个物品 $i$ ,体积 $v_{i}$ ,价值(权重) $w_{i}$ 。要求往背包内装物品,在装入背包物品总体积不超过背包体积的前提下,求背包内物品价值总和的最大值
- 分析思路
- 状态表示
- 可以把动态规划思想理解为每一个局面的最佳规划取决于上一个选择更少、更加简单的局面,不断递归求出题目给出局面的最佳规划
- $f(c_{1},c_{2},…,c_{n})$ 表示一个 $n$ 维的局面
- 每一维是用来确定局面的一个条件(比如背包问题的局面由容量和选择范围确定)
- 每个局面是所有满足条件的规划的集合
- 每个集合有特定的属性,比如最大值,最小值,最多数量等,一般由题目要求的量决定
- $f(c_{1},c_{2},…,c_{n}) = character$ 存储属性
- 状态计算(状态转移方程)
- 对应局面表示的集合的划分
- 每个集合可以划分成更小的集合,这些小集合对应的每一维的条件也更小
- 一般可以以一维条件的改变来划分集合
- 集合划分后,由更小的集合的属性来递推大集合的属性
- 特点:每个物品只有一件
- 二维动态规划
- 状态表示
- 集合:$f(i,j)$ , 表示从 $1$ ~ $i$ 号物体之间选择,体积总和不大于 $j$ 的所有选法组成的集合
- 属性:价值最大值
- 状态计算:集合的划分
- 从 $1$ ~ $i$ 号物体之间选择,体积总和不大于 $j$,可以是从 $1$ ~ $i-1$ 号物体之间选择,体积总和不大于 $j$;也可以是必选第 $i$ 号物体,然后从 $1$ ~ $i-1$ 号物体之间选择,体积总和不大于 $j-v_{i}$,
- 因此
f(i,j)
可以从 f(i-1,j)
转移过来,也可以从 f(i-1,j-v[i])
转移过来。方程如下
f(i,j)=max(f(i-1,j),f(i-1,j-v[i])+w[i])
- 一维优化
- 可以发现,二维做法时,第一维只用到了 $i$ 和 $i-1$ ,因此每次更新只需要将原来的值覆盖即可,不需要存储,因此第一维可以去掉
- 在更新第二维时,
f[j] = max(f[j],f[j - v[i]] + w[i])
- 如果不选 $i$ ,即不考虑加入新的物品,则
f[j]=f[j]
- 如果选 $i$ ,则需要 $i-1$ 状态下的
f[j - v[i]]
;由于 $j-v_{i}<j$ ,因此在 $i$ 状态下时,f[j]
一定要比 f[j - v[i]]
先更新,所以 $j$ 需要倒序遍历
- 代码
- 特点:物品件数无限制
- 二维状态 + 三重循环(TLE)
- 状态表示
- 状态计算
- 按 $i$ 的个数为 $0,1,2,3,…,k(k\times v_{i}<j)$ 来划分集合
f(i,j) = max(f(i - 1,j - 0 * v[i]) + 0 * w[i],f(i - 1,j - 1 * v[i]) + 1 * w[i],f(i - 1,j - 2 * v[i]) + 2 * w[i],...,f(i - 1,j - k * v[i]) + k * w[i])
- 二维状态 + 二重循环
f(i,j)= max(f(i-1,j-0*v[i])+0*w[i],f(i-1,j-1*v[i])+1*w[i],f(i-1,j-2*v[i])+2*w[i],...,f(i-1,j-k*v[i])+k*w[i])
f(i,j-v[i])= max(f(i-1,j-1*v[i]),f(i-1,j-2*v[i])+1*w[i],...,f(i-1,j-k*v[i])+(k-1)*w[i])
- 可以发现,
f(i,j) = max(f(i - 1,j),f(i,j - v[i]) + w[i])
由此只需要两重循环
- 与 $01$ 背包方程的区别:$01$ 背包中条件为
j - v[i]
的状态是 $i-1$ 条件下的,而完全背包是 $i$ 条件下的
- 一维状态 + 二重循环
- 完全按照 $01$ 背包问题的优化方式来优化
- 唯一不同在于,遍历 $j$ 时需要从小到大,理由是$01$ 背包中条件为
j - v[i]
的状态是 $i-1$ 条件下的,而完全背包是 $i$ 条件下的,因此 $j - v[i]$ 需要比 $j$ 更早更新
- 代码
- 特点:每个物品 $i$ 有 $s_{i}$ 件
- 动态规划
- 状态表示
f[i,j]
- 集合:所有只从前 $i$ 个物品中选,且总体积不超过 $j$ 的选法
- 属性:$Max$
- 状态计算
- 按 $i$ 的个数为 $0,1,2,3,…,s_{i}$ 来划分集合,这一点与完全背包问题几乎一样
- 方程:
f[i,j]=max(f[i-1,j],f[i-1,j-v[i]]+w[i],f[i-1,j-2*v[i]]+2*w[i],...,f[i-1,j-s*v[i]]+s*w[i])
- 代码
- 对前一题的优化
- 不能按照完全背包的优化方式来优化,因为物品个数有限制,因此
f[i,j-v[i]]
最后会多出一项 f[i-1,j-(s+1)v[i]]+s*v[i]
,就不能直接替换优化了
- 优化方法:二进制优化
- 把 $s_{i}$ 分解为 $1,2,4,8,…,2^{k},s_{i}-(2^{k+1}-1)$ ,这 $k+2$ 个数可以在每个数只用一次的前提下组合出 $1$ ~ $s_{i}$ 之间的所有数
- 这样不仅 $s$ 的规模降到了 $\log s$ ,且整体的问题也等价于 $01$ 背包问题,就可以直接使用 $01$ 背包问题的优化解法
- 代码
- 物品被分成了几组,每一组里只能选一件物品
- 动态规划
- 状态表示
f[i,j]
- 集合:只从前 $i$ 组物品中选,且总体积不大于 $j$ 的所有选法
- 属性:$Max$
- 状态计算
- 划分:按第 $i$ 个物品从第 $i$ 组中不选或选哪个来划分
- 方程:
f[i,j]=max(f[i-1,j],f[i-1,j-v[i,k]]+w[i,k])
- 代码