[集训整理]QBXT - DP - DAY1
#1.0 上午
#1.1 亿些题目
#1.1.1 热身题
开场先是一个 \(\text{Catalan}\) 数:
求长度为 n 的合法括号序列有多少个,对 10^9 + 7 取模。
于是便可以简单的设计状态:
\(f_i\) 表示长度为 \(i\) 的合法括号序列的个数,那么有
将上式分为两部分:
这一部分枚举断点;
这一部分是形如
正确性:第一部分正确性显然,我们来看第二部分:
问题其实为如何保证这样的串不能从中间任何一点分成两个合法的串(即需保证不与第一种情况重复)。考虑一个括号串为合法串,其必然满足任意位置前左括号的数量大于等于右括号的数量。那么假设一个如上形式的串可以分成两个合法的串,那么由以上性质立刻知道:去掉左右两边的括号后的括号串必然不是合法括号串,于是第二部分正确性显然。
那么,换个思路:
有时候,画出转移的图像有助于我们解决某些 DP 问题
顺着上面性质来看,画出下图(\(Dyck\) 路?):
其中横坐标为左括号的个数,纵坐标为右括号的个数,可知答案为不碰到红线到达 \((n,n)\) 的路径条数。从 \((0,0)\to(n,n)\) 的路径条数共有 \(\tbinom{2n}{n}\) 种,再考虑碰到红线的路径条数,将任意一条这样的路线关于红线对称,会发现终点总是到达 \((n-1,n+1)\),于是便是到达 \((n-1,n+1)\) 的路径总数。故答案为 \(\tbinom{2n}{n}-\tbinom{2n}{n-1}\),也便是 \(\text{Catalan}\) 数。
#1.1.2 LCS 计数
设计状态
- \(f_{i,j}\) 表示 \(S\) 在 \(1\sim i\),\(T\) 在 \(1\sim j\) 中的 \(\text{LCS}\) 长度,
- \(g_{i,j}\) 表示 \(S\) 在 \(1\sim i\),\(T\) 在 \(1\sim j\) 中的 \(\text{LCS}\) 个数,
考虑转移(只讨论 s[i]!=t[j]
的情况)
if (s[i] != t[j]){
f[i][j] = max(f[i - 1][j],f[i][j - 1]);
if (f[i - 1][j] > f[i][j - 1])
g[i][j] = g[i - 1][j];
else if (f[i - 1][j] < f[i][j - 1])
g[i][j] = g[i][j - 1];
else if (f[i][j] != f[i - 1][j - 1])
g[i][j] = g[i - 1][j] + g[i][j - 1];
else g[i][j] = g[i - 1][j] + g[i][j - 1] - g[i - 1][j - 1];
}
前两个 if (...)
正确性显然,后两个考虑 \(\text{LCS}\) 是否以 \(s[i]\)(\(t[j]\))结尾。
#1.1.3 [POI2017] Flappy Bird
坐标轴转换!!!
。。。DP(?课后老师说是贪心)不大懂,再看看题解罢。。。
#1.1.3 [CQOI2007] 涂色 & [CF1132F] Clear the String
直接令 \(f_{i,j}\) 表示将区间 \([i, j]\) 染色成目标状态的最 少操作次数。首先,如果不存在一次操作将 \([i, j]\) 涂成了一种颜 色,那么一定存在一个位置可以将区间分成两块分别处理,即 \(f_{ij} = \min(f_{i,k} + f_{k+1,j})\)。观察到如果 \(s_i = s_j\),那么 \(f_{i,j}\) 还可以从 \(f_{i+1,j}\) 和 \(f_{i,j+1}\) 转移得到。
\(f_{l,r}\) 表示删去 \([l, r]\) 的子串的最小代价。 考虑转移: 首先可以直接删去尾,即 \(f_{l,r} = f_{l,r−1} + 1\)。然后考虑枚举中间哪个 字符和 \(r\) 位置的字符一起被删去,假设枚举的位置是 \(k\),那么 \(f_{l,r} = f_{l,k} + f_{k+1,r−1}\),因为 \(s_k = s_r\),所以两个位置可以一起删去而 不需要耗费额外的代价。
当然,第二题也可以用第一题的思路类似地处理
#1.1.4 [AHOI2009] 中国象棋
转换题目中的限制为:每一行每一列的炮的数量不能超过 \(2\)。
那我们可以设计状态:\(f_{i,j,k,l}\) 表示当前放到了第 \(i\) 行,其中有 \(j\) 列放了 \(0\) 个,有 \(k\) 列放了 \(1\) 个,有 \(l\) 列放了 \(2\) 个。不难发现,上面的 \(j,k,l\) 之和为列数,于是上面三个可知二推一,于是简化状态: \(f_{i,j,k}\) 表示当前放到了第 \(i\) 行,其中有 \(j\) 列放了 \(0\) 个,有 \(k\) 列放了 \(1\) 个。
于是有转移方程:
#1.1.5 [arc074e] RGB Sequence
如何记录区间内不同颜色的数量:分别保存各种颜色最后一次出现的位置。
设 \(f_{i,j,k,l}\) 表示当前考虑到了第 \(i\) 个格子,红绿 蓝三种颜色最后一次出现的位置分别是 \(j\),\(k\), \(l\) 的方案数。
依然考虑去除冗余状态,发现 \(i=\max\{j,k,l\}\),于是可简化为:设 \(f_{i,j,k}\) 表示当前考虑到了第 \(\max\{i,j,k\}\) 个格子,红绿 蓝三种颜色最后一次出现的位置分别是 \(i,j,k\) 的方案数。
#1.2 一些卡常技巧
- 别开大数组!
- 滚动数组能滚就滚
- 动态管理内存(
queue,deque
)比大数组更差 DFS
不要乱用
#2.0 简单整理
题太多了,一个一个整整不完,下面之整理些重点
- 遇到难以解决的柿子,考虑其几何意义,将其进行转换,变为好解决的问题。
- 如 [NOI2009] 管道取珠
- 对于一些含幂的较大数,可以按进制位进行思考.
- 如何记录区间内不同颜色的数量:分别保存各种颜色最后一次出现的位置。
- 画出转移的图像有助于我们解决某些 DP 问题.