动态规划

type
status
date
slug
summary
tags
category
icon
password
类别
内容


入门DP

  • 如果某一问题有很多重叠子问题,使用动态规划是最有效的。
  • 动态规划中,每一个状态一定是有上一个状态推导出来的,这一点区分于贪心。
动态规划五步曲
  1. 状态表示:确定dp数组以及下标的含义
  1. 状态计算:确定递推公式
  1. 初始化:dp数组如何初始化
  1. 确定遍历顺序
  1. 动态演示:举例推导dp数组(草稿纸或打印输出)
斐波那契数列
爬楼梯
📌
后续的DP问题中,不会详细写出动态规划五步曲,主要注重状态表示,状态计算以及状态初始三部分,其它做到自己做到心里有数即可

背包DP

notion image
📌
由上图可知,背包问题的不同,是根据每个物品的数量来决定的
01背包
每个物品只有一个
  1. 状态表示
notion image
  1. 状态计算(迂回思想
notion image
  1. 遍历顺序
notion image
模板代码
 
📌
从代码中,可以发现,我们只需要维护当前这一行和上一行的状态,因此可以利用 将代码优化为一维。同时要注意数据污染问题
notion image
代码如下:
完全背包
每件物品有无穷多个
  1. 状态表示
notion image
  1. 状态计算
notion image
  1. 遍历顺序
notion image
📌
这里同样可以使用滚动数组来进行一维优化,但这里不用要求背包容量j一定要从大到小遍历,因为我们可以从下图看出,完全背包虽然会使用到前面容量较小的背包,造成同一件物品添加多次,但这正是完全背包允许的呀
notion image
notion image
多重背包
第i中物品最多有si件
  1. 状态表示
notion image
  1. 状态计算——增加一层循环,对物品数量进行选择,进而转换为01背包问题
📌
由于三重循环,会导致时间复杂度过高,当数据量过大时,会造成超时,因此接下来使用 来去掉第三重循环
notion image
notion image
另外可以通过 来进行优化
混合背包
题目中,同时存在01背包、完全背包和多重背包
解题策略:在数据输入时,先将多重背包通过二进制拆分转换01背包,然后DP时,分成01背包和完全背包两种问题进行解决
分组背包
物品在不同的组中,且每一组至多选择一件物品
  1. 状态表示
notion image
  1. 状态计算
notion image

线性DP

最长上升子序列
  1. 状态表示:f[i]表示以第i个元素结尾的最长上升子序列的长度
  1. 状态计算:f[i] = max(f[i], f[j] + 1;
提高:上面的算法复杂度为:O(n ^ 2), 利用二分法优化此题-----可以适用于数据量更大的题目
  • 二分法中,只能找出最长上升子序列的长度,但是没办法找到子序列
  • 大则添加
  • 小则替换(二分法的体现)
最长公共子序列
  1. 状态表示:f[i][j]表示以i位结尾的字符串和以j为结尾的字符串的最长公共子序列长度
  1. 状态计算
    1. f[i][j] = max(f[i][j], f[i - 1][j - 1] + 1);
    2. f[i][j] = max(f[i - 1][j], f[i][j - 1]);
提高:输出最长公共子序列
notion image
 
最长公共字串
区分序列和字串
  • 公共序列:不要求连续
  • 公共字串:要求连续
  1. 状态表示:f[i][j]表示以i为结尾的字符串和以j为结尾的字符串的最长公共字串的长度
  1. 状态计算:
    1. f[i][j] = 0;
    2. f[i][j] = max(f[i][j], f[i - 1][j - 1] + 1);
最短编辑距离
  1. 状态表示
notion image
  1. 状态计算
notion image
  1. 手动推演
notion image
还可以利用滚动数组进行优化,在此不做代码演示
notion image
题目变式:编辑距离
 

区间DP

【模板题】石子合并
  1. 状态表示
notion image
  1. 状态计算
notion image
notion image
 
notion image
  1. 初始状态
notion image
环形石子合并
notion image
这道题目和模板题的区别就在于一个是线性,一个是成环的,这时候我们很自然可以想到,在每个两点之间切断,变成线性之后使用模板代码即可:
notion image
📌
时间复杂度过高,会超时
📌
环形转化为链形:复制一遍数组,转化为长度为2N的链形数组 初始化会有所不同
notion image
能量向量
notion image
注意此题与环形石子合并的区别:
  • 环形石子合并的代价都在石子本身
  • 此题的代价都在项链石子的两侧,所以主体变为了两侧的能量值不用进行初始化,个数也由n变为了n + 1,同时l,r两个也对应发生了改变
  • 同时注意:这里的能量计算,必须涉及左、右、中三者之积,所以l < k < r,k不能取边界值

树形DP

概念
线性DP子结构是一条线段,树形DP子结构是一颗子树
一般思路:从分析子树入手
  • 状态表示通常是与子树根节点u有关的函数
  • 状态计算就是寻找根点与子节点以及边权的递推关系
  • 编写代码时,通常要dfs,从根到叶,再从叶到根,在合适的地方DP
📌
概念不容易入手时,建议从题目入手
没有上司的舞会
  1. 题目示意图
notion image
  1. 状态表示
notion image
  1. 状态计算
notion image
  1. 代码编写
notion image
  1. 代码推演
notion image
有依赖的背包

状压DP

概念
核心:用二进制表示状态,用十进制数存储状态
判断约束条件时,要用到 ,学习状压DP,要先学会简单的位运算
判断约束条件时,要用到 ,学习状压DP,要先学会简单的位运算
解题思路:
  1. 列举每行所有的状态(有2^n个状态),找出符合题意的合法状态,然后写出行内合法的位运算表达式
  1. 找出行间兼容的位运算表达式
  1. 列出状态表示
  1. 状态计算
代码编写:
  1. 先对每一行的合法状态进行预处理
  1. 多重循环进行状态计算
notion image
小国王
  1. 行内合法
notion image
  1. 行间兼容
notion image
  1. 状态表示
notion image
  1. 状态计算
notion image
玉米田
  1. 行内合法
notion image
  1. 行间兼容
notion image
  1. 状态表示
notion image
  1. 状态计算
notion image
炮兵部队
  1. 行内合法
notion image
  1. 行间兼容
notion image
  1. 状态表示
notion image
  1. 状态计算
notion image

数位DP

概念
数位DP特点:求某个区间[L, R]内,满足某种性质的数的个数
解题技巧:
  • 类似前缀和的思想,将区间[L, R]问题转换为[0, R] - [0, L - 1]求解
  • 从高位到低位填数,要分类讨论
notion image
notion image
数字游戏
本题无题目链接,题目如下:(代码在输入方面有些许变化)
本题无题目链接,题目如下:(代码在输入方面有些许变化)
notion image
 
 
  1. 状态表示
notion image
  1. 状态计算
notion image
Windy数
  1. 状态表示
notion image
  1. 状态计算
notion image
notion image
 
notion image
 
计数问题

记忆化搜索

概念
特点:
  • 从上向下的累加和是不能重复利用的
  • 从下向上的累加和是能重复利用的
意义:对递归树做了剪枝,搜索过的子树不再重复搜索,直接返回存储值
数字三角形
拓展
  1. 记录最大和的路径
  1. 左右走的次数有限制
notion image
经观察得:
  • n为偶数时,最后一层落在的点一定在n/2或n/2+1
  • n为奇数时,最后一层落在的点一定在n/2+1
这个时候只能从上往下遍历
滑雪
 
成瘾与戒瘾图论