序列dp

LCS问题

题目1

给定 \(A,B\) 两个排列,求它们的 \(\text{LCS}\)

题解1

两个序列都是排列,那么可以考虑建立映射关系跑LIS

题目2

给定 \(A,B\) 两个序列,求它们的 \(\text{LCS}\)

题解2

就是个简单的多序列dp

\(f[i][j]\) 表示考虑了 \(a\) 序列的前 \(i\) 个数, \(b\) 序列前 \(j\) 个数的最长公共子序列的长度,转移显然是

\[f[i][j]=\max\lbrace f[i-1][j],f[i][j-1],f[i-1][j-1]+[i=j]\rbrace \]

CF314E Sereja and Squares(括号序列问题套路1)

题目

给定一个长度为 \(n\) 的仅包含左括号和问号的字符串,将问号变成左括号或右括号使得该括号序列合法,求方案总数

题解

  • 括号序列问题,往往就是把左括号看成 \(+1\) ,右括号看成 \(-1\) ,我们只需要保证任意一个前缀大于等于 \(0\),且总和为 \(0\) ,就代表是个合法括号序列了

\(f[i][j]\) 表示当前到第 \(i\) 个字符,现在还有 \(j\) 个左括号,那么分三种情况考虑

  1. 若第 \(i+1\) 个字符是左括号,则能转移到 \(f[i+1][j+1]\)
  2. 若第 \(i+1\) 个字符是右括号,则能转移到 \(f[i+1][j-1]\)
  3. 若第 \(i+1\) 个字符是问号,则能转移到 \(f[i+1][j-1]\)\(f[i+1][j+1]\)

对于这个问题,只需要考虑 \(1,3\) 转移,最终 \(f[n][0]\) 就是方案总数,时间复杂度为 \(\mathcal O(n^2)\)

BZOJ4922 Karp-de-Chant Number(括号序列问题套路2)

题目
给出一些括号序列,要求选择一些括号序列拼接成一个合法的括号序列,使得总长最大

长度与个数均不超过 \(300\)

题解

首先将每个括号序列合法的括号对进行一个消除,那么最终得到的括号序列一定形如 $ ))) \cdots (($,于是我们对每个括号序列记两个值 \(x,y\) 表示左括号与右括号的个数,于是我们就是要最大化我们的括号序列配对数,参考上一题,我们在选择括号序列的同时,一定要保证所有时刻的 \(x-y\ge 0\),且最终的 \(x-y=0\)

这像极了P4025 [PA2014]Bohater,这里简述一下贪心思路:

  1. 先排 \(x-y\le 0\) 的,再排 \(x-y<0\)
  2. 对于 \(x-y\le 0\) 的,按照 \(y\) 从小到大排
  3. 对于 \(x-y< 0\) 的,倒着考虑,发现本质是将上一种情况的 \(x,y\) 对换了,按照 \(x\) 从到小排即可

转移和上题大同小异,不在赘述

P4933 大师(特殊的子序列提取问题)

题目

给定一个长度为 \(n\) 的非负整数数列,求它有多少个等差子序列。最大值 \(v\) 不超过 \(2\times 10^4\)

题解

一个朴素的想法是,设 \(f[i][d]\) 表示考虑到第 \(i\) 个数,公差是 \(d\) 的子序列的个数,转移显然有

\[f[i][d]=\sum_{j<i\And a_i=a_j+d}(f[j][d]+1) \]

时间复杂度是 \(\mathcal O(n^2v)\) 的,但是我们发现,以 \(i\) 结尾的等差数列的数量是明显不超过 \(i-1\) 的,所以我们考虑给转移方程换一种写法

\[f[i][a_i-a_j]=\sum_{j<i}(f[j][a_i-a_j]+1) \]

于是时间复杂度降为 \(\mathcal O(n^2)\) ,空间复杂度为 \(\mathcal O(nv)\) ,可以通过本题

P1874 快速求和(子段划分+背包)

题目

给定一个数字串 \(S~(S\le 40)\) ,要求加入最少的加号来使其变成一个数 \(t~(t\le 1\times 10^6)\) ,求这个最小值,无解输出 \(-1\)

题解

\(f[i][s]\) 表示考虑了前 \(i\) 个位置的数,最终构成的数为 \(s\) 的最小值,\(\text{cost}[i][j]\) 表示从 \(i\)\(j\) 构成的数的值,于是有转移方程为

\[f[i][s]=1+\min_{j<i} f[j][s-\text{cost}[j+1][i]] \]

从后往前枚举 \(j\),超过 \(t\) 便不再继续,时间复杂度为 \(\mathcal O(nt\log t)\)

P2758 编辑距离(经典多序列问题)

题目

给定字符串 \(A,B\),可以对 \(A\) 进行如下操作

  1. 插入一个字符
  2. 删除一个字符
  3. 将一个字符替换成另一个字符

求将 \(A\) 变化成 \(B\) 的最小步数

字符串长度不超过 \(1000\)

题解

\(f[i][j]\) 表示将 \(A\) 字符串的前 \(i\) 个字符转化成 \(B\) 字符串中的前 \(j\) 个字符所需要的最小操作次数,转移如下

\[f[i][j]=\min\lbrace f[i-1][j]+1,f[i][j-1]+1,f[i-1][j-1]+[i\not ={j}]\rbrace \]

具体解释下的话是

  1. \(f[i-1][j]\) : \(A\) 的前 \(i-1\) 位与 \(B\) 的前 \(j\) 位对上了,现在要在 \(A\) 中删除一个字符
  2. \(f[i][j-1]\) \(A\) 的前 \(i\) 位与 \(B\) 的前 \(j-1\) 位对上了,现在要在 \(A\) 中插入一个字符
  3. \(f[i-1][j-1]\) 直接把 \(A_i\) 变成 \(B_j\),或者不用变

P2679 [NOIP2015 提高组] 子串

题目

有两个仅包含小写英文字母的字符串 \(A\)\(B\)

现在要从字符串 \(A\) 中取出 \(k\) 个互不重叠的非空子串,然后把这 \(k\) 个子串按照其在字符串 \(A\) 中出现的顺序依次连接起来得到一个新的字符串。请问有多少种方案可以使得这个新串与字符串 \(B\) 相等?

\(1\le n\le 1000,1\le m\le 200,1\le k\le m\)

题解

\(f[i][j][k]\) 表示 \(A\) 串考虑到第 \(i\) 位,匹配到 \(B\) 串的第 \(j\) 位,选了 \(k\) 个字符时的方案数

但是,观察样例, \(\underline{aa}\)\(\underline{a}~\underline{a}\) 是两种方案,因此我们给我们的状态新增一维成 \(f[i][j][k][0/1]\) 表示第 \(i\) 位选还是不选

于是有

\[\begin{cases} f[i][j][k][0]=f[i-1][j][k][0]+f[i-1][j][k][1]\\ f[i][j][k][1]=f[i-1][j-1][k-1][0]+f[i-1][j-1][k][1]+f[i-1][j-1][k-1][1]~~~(a[i]=b[j]) \end{cases} \]

滚掉 \(i\) 这一维,时间复杂度是 \(\mathcal O(nmk)\) ,空间复杂度是 \(\mathcal O(mk)\)

P1435 [IOI2000] 回文字串 / [蓝桥杯 2016 省] 密码脱落 (回文串问题转成最长公共子序列问题)

题目

给定一个长为 \(n~(n\le 1000)\) 的字符串,要求在其中插入字符使得其成为一个回文串,最小化插入的字符数

题解

一种sb做法是,设 \(f[l][r]\) 表示区间 \([l,r]\) 成为回文串所需要的最下插入数,然后讨论一下就行

if(s[l]==s[r]) f[l][r]=f[l+1][r-1];
else f[l][r]=min(f[l+1][r],f[l][r-1])+1; 

一种比较优美的做法是,我们令原来的字符串为 \(A\) ,其翻转后的字符串是 \(B\) ,那么这个问题与求 \(A,B\) 的最长公共子序列是等价的

那么我们求出 \(\text{LCS}\) 后拿字符串长度减去 \(\text{LCS}\) 的长度就是答案

CF568E Longest Increasing Subsequence (最大化最长上升子序列长度+dp方案输出)

题目

给定一个长度为 \(n\) 的有 \(k\) 个空缺的序列。

你有 \(m\) 个数可以用于填补空缺,且不可以使用相同的数

要求最大化最长上升子序列的长度。

\(n, m \le 10^5\)\(k \le 10^3\)

题解

首先,由于两个同样的元素填到序列里面对答案是没有任何贡献的,因此我们得到最优填法所使用的数必然两两不同

\(l[i],p[i]\) 表示考虑到第 \(i\) 位时最长上升子序列的长度和上一项的位置,\(f[i],g[i]\) 表示长度为 \(i\) 的上升子序列的最后一项的最小值和它所在的位置,假设我们现在从前向后考虑到第 \(i\) 个位置

  • 若该位置上的数为 \(x\) ,那么在 \(f[i]\) 上二分找到 \(<x\) 的最大的 \(f[j]\) ,然后依次更新: \(l[i]=j+1,p[i]=g[j],f[j+1]=x,g[j+1]=i\)
  • 若该位置是空缺的,枚举我们要填补的数为 \(x\) 找到填补空缺的数中 \(<x\) 中最大的 \(f[j]\) 更新为 \(f[j+1]=x,g[j+1]=i\),由于用于填补空缺的数随 \(i\) 的增大是是单调递增的,所以这里可以使用指针处理,时间复杂度 \(\mathcal O((n+m)k)\)

考虑如何还原,由于我们求 \(LIS\) 的DP过程中是将 \(i\) 作为了一个 \(LIS\) 的终止节点,所以我们自然可以考虑倒序还原

若该位置是空缺的,则先在它前面的不是空缺的位置里找到位置 \(\text{pos}\) 满足 \(l[\text{pos}] = i - 1\) ,同时 \(\text{pos}\) 上的数 \(< x\)。如果找到了,那么上一项的位置就是 \(\text{pos}\) ;否则,上一项的位置就是上一个空缺的位置,且这个空缺上的数为用于填补的数中 \(< x\) 的最大的数

时间复杂度 \(\mathcal{O} (n\log n+m\log m+(n+m)k)\)

HDU 1421 搬寝室 (通过特殊性质将分组问题转为dp问题)

题目

\(n\) 个行李,每个行李有一个重量。

在你要去搬行李,要搬 \(2k\) 个行李,每次要搬 \(2\) 个,假设这两次搬得行李的重量为 \(x,y\) ,那么我们称这一次搬行李的疲惫度为 \((x-y)^2\)

要求最小化疲惫度之和

\(2\le 2k \le n\le 2000\)

题解

首先我们注意到dp是很难做 \(2k\) 个物品进行分组的问题的,所以我们要考虑挑了 \(2k\) 个行李出来之后怎样分组是最优的

假设有 \(4\) 个行李的重量从小到大分别为 \(a,b,c,d\) ,我们发现, \((a,b),(c,d)\) 这样分组一定是更优的,证明可以考虑作差法,不再赘述

于是我们设 \(f[i][j]\) 表示考虑完前 \(i\) 个数,选出了 \(j\) 对的方案数,先对 \(n\) 个数升序排序,然后通过分讨选不选 \(i\) 进行转移

  1. 如果将第 \(i\) 个数试做一对中较大的那个数,那么显然它与 \(i-1\) 配对是一定是比跟前面的数配对更优的,同时如果此时要求配成 \(j\) 对,那么就就要保证前 \(i-2\) 个数可以配成 \(j-1\) 对,于是我们的转移如下

\[f[i][j]=f[i-2][j-1]+(a[i]-a[i-1])^2 \]

  1. 如果不选第 \(i\) 个数,那么转移如下

\[f[i][j]=f[i-1][j] \]

时间复杂度 \(\mathcal{O}(n^2)\)

BZOJ 1786 配对 (最小化逆序对数)

题目

给你一个长度为 \(n\) 的序列,现在有一些位置未知,要求在这些未知位置上填数,使得最终序列产生的逆序对数最小

保证所有的数都在 \([1,k]\) 范围, \(k\) 给定

\(n\le 1\times 10^4,k\le 100\)

题解

首先考虑一个性质,我们填进去的数一定是单调不降的,证明可以考虑交换填进去的两个数 \(a,b\) ,交换完之后, 中间的数的逆序对是 \(\ge a\) 的数加上 \(\le b\) 的数,由于 \(a\le b\) ,那么这显然比交换前的逆序对数更大。

所以我们每填进去一个数只会对被最开始序列中就有的那些数贡献逆序对数,假设我们填的数是 \(j\) ,那么求一下前缀 \(\ge j\) 的数的个数加上后缀 \(\le j\) 的数的个数即可,可以预处理一下

时间复杂度 \(\mathcal{O}(nk)\)

Atcoder Beginner Contest 320 F (序列上路径dp)

题面

给定 \(n\) 个坐标 \(x_1\sim x_n\) ,你初始在位置 \(0\) ,有油 \(h\) 升,开车每行驶一个单位长度就要消耗一升油,你要从 \(0\)\(x_n\) 后再返回 \(0\)\(x_1\sim x_{n-1}\) 位置上都是加油站,第 \(i\) 个加油站可以通过支付 \(p_i\) 的代价使当前油量 \(h'\) 变为 \(\min(h,h'+F_i)\) 。每个加油站只能使用一次,问往返的最小代价

\(n,h\le 300\)

题解

有一个比较显然的dp,设 \(f[i][j][k]\) 表示从 \(i\)\(n\) 再返回 \(i\) ,其中初始油量为 \(j\) ,返回油量为 \(k\) 时的最小花费

转移时讨论是否加油,设 \(d_i=x_{i}-x_{i-1}\) ,那么

  • 如果不加油,转移为

\[f[i][j][k]\leftarrow f[i-1][j+d_i][k-d_i] \]

  • 如果从 \(i\) 出发时加油,转移为

\[f[i][\min(j+F_i~,h)][k]\leftarrow f[i-1][j+d_i][k-d_i]+p_i \]

  • 如果返程回到 \(i\) 时加油,转移为

\[f[i][j][k]\leftarrow f[i-1][j+d_i][\min(k+F_i~ ~,h)-d_i]+p_i \]

时间复杂度 \(\mathcal O(nh^2)\)

posted on 2023-10-27 18:00  star_road_xyz  阅读(28)  评论(0编辑  收藏  举报

导航