开拓计划3 - DP入门
开拓计划3 - DP入门
DP
DP的概念
- Q:什么是DP?
- A:动态规划(DP),是指在实现问题时有多种方法,而每次都取最优的方案,解决问题。
- Q: DP和贪心有什么区别?
- A: DP是将问题分成很多类方案再整合出最优的,贪心是将问题分成很多步每一步最优推出全局最优。
DP五部曲
- 设定状态:根据题目需要,设定 \(dp_{i,j,k,......}\) 表示的含义。一般问什么设什么。
- 递推公式:将当前状态分成几类实现方案,一类就是一种转移。
- 初始化:确定 \(dp\) 数组应当如何设定初始值。
- 遍历顺序:这点容易被忽视,却至关重要,特别是在背包问题中。
- 输出答案:答案有时需要单独在数组里面找,而不是直接输出数组的某一个位置。
NKOJ 1796 【USACO1.5.1】Number Triangles 数字金字塔
思路:典型的二维DP
实现方法
- \(dp_{i,j}\) 表示走到第 \((i,j)\) 个点的时候能取得的最大价值。
- 有两种方案,从正上方和左上方,递推公式:\(dp_{i,j}=\max(dp_{i-1,j},dp_{i-1,j-1})+arr_{i,j}\) 。
- 取最大值且没有负数,不用单独赋初始值。
- 从头到尾遍历。
- 答案是最后一排的所有数中的最大值。
NKOJ 1050 马拦过河卒(NOIP)
思路:同NKOJ 1796
区别
- 更改枚举顺序(从八个方向)和DP数组含义
NKOJ 1001 最小乘车费用
思路:典型的线性DP
实现方法
- 定义 \(dp_i\) 表示乘坐 \(i\) 站时的最小乘车费用。
- 有多种方案,直接乘坐或换乘。
- 初始值为无穷大。
- 从头到尾遍历 \(i\),从 \(\min(i-10,0)\) 枚举到 \(i\) 。
- 答案是\(dp_n\) 。
NKOJ 1043 最大连续子序列
思路:贪心 / DP
贪心版
- 遇到数,如果加上第 \(i\) 位整体变成负的了,那对后续没有好处,不如放弃这一整段从第 \(i+1\) 位重新开始。
DP版
- \(dp_i\) 表示当第 \(i\) 个数必须取得时候,能得到的最大字段和。
- 有两种方案,自己新开一段或跟着上一段的末尾,递推公式:\(dp_i=\max(dp_{i-1}+arr_i,arr_i)\) 。
- 由于有可能有负数,所以 \(dp\) 数组应当初始化为负无穷。
- 从头到尾遍历。
- 答案是整个数组中的最大值。
NKOJ 1049 【NOI1997 Day2 T1】最佳游览
思路:同NKOJ 1043
区别
- 输入的是二维数组,要先求出每一列的最大值,将最大值存下来,再在最大值数组中寻找最大字段和。
LIS
LIS的定义
- Q:什么是LIS?
- A:LIS(最长上升子序列),求一个序列中所有子序列里面,元素满足单调性的子序列,最长是多少。
LIS的求法
- 设 \(dp_i\) 表示以 \(arr_i\) 结尾的最长上升子序列的长度。
- 有两种方案。
- 自己单飞。
- 跟前面的连。
得到
\[dp_i =\max(1+dp_j,dp_i)\text{ 当}arr_i>arr_j\text{且} 1\le j \le i
\]
时间复杂度 \(O_{n^2}\) 有点高。
LIS求法的优化
- 更改 \(dp_i\) 的含义。改为表示长度为 \(i\) 的最长上升子序列的尾元素。
- 有两种情况。
- 能接上一个,作出新贡献。
- 接不了找能接的,接上。
得到
\[\begin{cases}
dp_{++cnt}=arr_i & arr_i>dp_{cnt}
\\
dp_{\text{找到的能接的}}=arr_i & \text{other}
\end{cases}
\]
找的过程可以用二分,时间复杂度 \(O_{n\log n}\)。
NKOJ 2017 渡轮问题(变态版)
模板题。
NKOJ 2508 渡轮问题(弱数据版)
模板题。
NKOJ 1004 拦截导弹
模板题。
NKOJ 1042 合唱队形(NOIP)
前后各遍历一次,取和。
LCS
LCS的定义
- Q:什么是LCS?
- A:LCS(最长公共子序列),求两个序列中所有子序列里面,元素完全相同的子序列,最长是多少。
LCS的求法
- 设 \(dp_{i,j}\) 表示以 \(arr_i\) 和 \(brr_j\) 结尾时最长公共子序列的长度。
- 有两种情况。
- 当 \(arr_i=brr_j\) 时,直接加入LCS。
- 不然就取两个序列上一个的最大长度。
得到
\[dp_{i,j}=\begin{cases}
dp_{i-1,j-1}+1 & arr_i=brr_j
\\
\max(dp_{i-1,j},dp_{i,j-1}) & \text{other}
\end{cases}
\]
NKOJ 1051 最长公共子序列
模板题。
NKOJ 3636 三个序列的最长公共子序列
模板提,把两个改为三个。
NKOJ 1052 最长公共子串
模板题,子串要求连续,遇到不相等的直接等于零,可以压维。
NKOJ 1035 咒语
思路:LIS与LCS综合
实现方法
- 设 \(dp_{i,j}\) 为当 \(arr_i\) 必取,\(brr_j\) 不一定取的情况下的最长连续上升子序列的长度。
- 共两种情况,\(arr_i = brr_j\) 时前面最长的再 \(+1\) ,或 \(arr_i < brr_j\) 此时应当更新前缀最大值。
- 无初始值。
- 先遍历 \(j\) 再遍历 \(i\) 。
- \(dp\) 数组的最大值。