动态规划笔记
一、动态规划题型及出现场景
动态规划题型:线性、背包、树形、区间、状态压缩
CSP-J 3-4题,CSP-S 2-4题,GESP五-八级
- 背包问题:有n个物品,每个都能描述为选和不选
- 树形dp:起码有树,既不求并查集(连通性),也不求生成树,也不求LCA(求两点最近公共祖先),那就是树形dp
- 线性dp:不是背包、不是树形、题目描述没有区间,那么不是线性就是贪心
- 区间dp:起码有区间,并且要验证答案能否由任意小区间合并得到大区间
- 状态压缩:子集枚举的优化(剪枝),起码50%数据 n<=20, 100%数据 n<=100
二、常见题型的状态定义、初始化、转移
1. 最长上升子序列(线性枚举前面的)
定义:f[i] 表示以 i 结尾的最长上升子序列长度
初始化:f[i] = 1
状态转移: f[i] = max(f[i], f[j] + 1)
(i ∈ [1, n], j ∈ [1, i-1], a[i] > a[j])
答案:ans = max(ans, f[i])
优化:n * logn
通过 pos = lower_bound(f + 1, f + 1 + top, a[i]) - f
判断是否需要替换 f[pos] = a[i] 或者 f[++top] = a[i]
2. 最大子段和(仅和前一个有关)
定义:f[i] 表示以第 i 个数字结尾的最大子段和
初始化:无
状态转移:
f[i] = max(f[i - 1] + a[i], a[i])
f[i-1]+a[i]表示a[i]归纳入前一段,或者以自己作为一段的开头即:a[i]
答案:ans = max(ans, f[i])
3. 二维空间移动DP(经常和前一个、上面、左边等相邻位置相关)
举例:可以向右或向下移动,问走到右下角的方案数,那么就可以转变成一个点只能从左边或者上边走下来
定义:f[i][j]表示走到i行j列的最大值/方案数
初始化:无,看题意
状态转移:
最大值:f[i][j] = max(f[i - 1][j]上, f[i][j-1]左) + a[i][j]
方案数:f[i][j] = f[i - 1][j] + f[i][j - 1], f[1][1] = 1
答案:f[n][m]右下角
4. 二维木板线性DP(可以从一个木板/线段的左右端点落下)
定义:f[i][0]表示走到第i个木板的左端点最优值,f[i][1]表示走到第i个木板的右端点最优值
初始化:看题意
状态转移:
f[i][0] = max/min(f[i - 1][0] + 左到左距离, f[i - 1][1] + 右到左距离)
f[i][1] = max/min(f[i - 1][0] + 左到右距离, f[i - 1][1] + 右到右距离)
5. 01背包
定义:f[j]表示第i个物品时剩余j空间时的最大价值
初始化:看题意,正常是无
状态转移:
for(int i = 1; i <= n; i++)
for(int j = m容量; j >= w[i]重量; j--) // 01背包逆序遍历容量,因为逆着遍历只会覆盖i-1轮时的f[j],才能保证第i轮物品只拿一次
f[j] = max(f[j], f[j - w[i]] + v[i]价值)
6. 01背包按价值DP(重量w[i] > 1e8,避免f[j]爆内存)
P14920 [GESP202512 六级] 道具商店
需要有一个sum = n个物品价值总和,sum += v[i]
定义:f[j]表示第i个物品时价值为j所需的最小空间大小
初始化:f = inf, f[0] = 0
for(int i = 1; i <= n; i++)
for(int j = sum总共价值; j >= v[i]价值; j--)
f[j] = min(f[j], f[j - v[i]] + w[i])
for(int j = sum; j >= 0; j--)
if(f[j] < inf && f[j] <= m){
此时j就是最大价值,输出j,break;
}
7. 完全背包之最大值DP
定义:f[j]表示第i个物品时容量为j的最大价值
for(int i = 1; i <= n; i++)
for(int j = w[i]重量; j <= m容量; j++)
f[j] = max(f[j], f[j - w[i]] + v[i]价值)
8. 背包方案数
定义:f[j]表示第i个物品,容量为j时有多少种装载方案
初始化:f[0] = 1
循环:01就逆序,完全就正序
状态转移:f[j] += f[j - w[i]]

浙公网安备 33010602011771号