I.导入:

这是一个\(O(n^2)\)的状态和转移方程:

\[f(i,j)=\left\{
\begin{aligned}
f(i-1,j-1)+k \ (1\leq j)\\
\max_{k \in [0,i]}{f(i-1,k)} (j=1)
\end{aligned}
\right.\]

这个方程目测是\(\Theta(n^2)\)的,但是实际上,上面的那个方程只是把数组整体位移了,下面的方程只是在位移后的数组开端添上了一个数,这个完全可以通过队列来实现,\(+k\)的操作,用一个整体的差分量就能实现,时间复杂度\(O(n)\)。这个方法最伟大的一点在于,它的复杂度比状态维度还要低一维,这是让人难以想到这种方法的一大原因。

II.xj2020 画画

\(n<=5000, m<=1e7\).显然当两种颜色的 a 相等的时候,它们完全等价,所以我们建一个桶 \(b_i\) 表示长度限制为 i 的颜色个数,再设\(f_{i,j,k}\)表示 DP 到了 i 号格子,当前颜色的长度限制是 j (其实是有点 hash 的表示颜色),当前颜色已经连续了 k 个,则状态转移方程为:

\[f(i,j,k)=\left\{
\begin{aligned}
f(i-1,j,k-1) \ (1\leq k)\\
b_j\sum_{j'!=j}{\sum_{k'}{f(i-1,j',k')}} + (b_j-1)\sum_{k'}{f(i-1,j,k')} (j=1)
\end{aligned}
\right.\]

这个状态状态转移方程上面和下面各是\(\Theta(n^3)\)的,但是我们可以发现这个转移方程,上面的可以用队列来实现整体位移,下面的可以对每个队列维护一个和,再维护一下总和,就能实现\(\Theta(n^2)\)了,主要代码如下:

for (int i = 1; i <= n; i++)
{
q[i].push(b[i]);
lastsum += (S[i] = b[i]);
}
for (int i = 2; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
if (!b[j]) continue;
ins[j] = ((LL)lastsum * b[j] % mod + mod - S[j]) % mod;
}
for (int j = 1; j <= n; j++)
{
if (!b[j]) continue;
(S[j] += ins[j]) %= mod;
(lastsum += ins[j]) %= mod;
q[j].push(ins[j]);
}
for (int j = 1; j <= n; j++)
{
if (!b[j]) continue;
while (q[j].size() > j)
{
(S[j] += mod - q[j].front()) %= mod;
(lastsum += mod - q[j].front()) %= mod;
q[j].pop();
}
}
}
fout << lastsum << Endl;

实现难度不大,主要难度在于设出状态和方程,因为这个状态有三维,很多时候我们就默认它的时间复杂度大于等于三次方,而这题偏偏又很容易得到只有两维的状态设置,很多人以为“肯定不如二维的状态设置”,但实际上,这个三维的状态可以优化到二次方,而这个二维的状态设置没法实现\(\Theta(1)\)的转移

III.xj2020 字符串

\(n<=2e5,m<=20\).假设 DP 到了前 i 个点,当前的两个子序列,一个肯定有一个以 i 号字符串结尾,我们只需要存另一个子序列以什么结尾;而子序列的长度都相同,说明我们只关心这个子序列最后一个串是哪一个。设\(f[i][j]\)表示,DP 到了前 i 个点,一个子序列的结尾是 i,另一个子序列的结尾是 j 时的最短长度。综合把 i 接到 i-1 上 和把 i 接到 i-1 之前的两种情况,可以得到状态转移方程:

\[f(i,j)=\left\{
\begin{aligned}
\min\{f(i-1,k)+cost(k,i)\} \ (j=i-1)\\
f(i-1,k)+\text{cost}(i-1,i)(else)
\end{aligned}
\right.\]

这个状态转移方程本身已经很难想到了,需要很熟稔的分类讨论的能力。优化上,可以使用栈——上面那东西是整体加法,下面那东西是在栈底追加一个值。考虑这个 min 怎么\(\Theta(1)\)求:对于 cost 相等的 k ,显然它们是等价的。所以把已有的字符串倒序放进一个 Trie 里面,查询时正着顺着 \(S_i\) 走,每到一个点用\(m-dep+f_X\)更新答案,其中\(f\)是树上前缀最小值,这样会有重复情况,但是显然不影响。用整体差分就能实现 f 值的修改。

IV.「2020-09-20 五校联考」球与洞 (ballhole)

这个是国冬模拟费用流的第二题,居然被搬来了五校联考。。。

首先使用微扰法,把球和洞画成两排,把每个球连向它的洞,显然这些连线不能相交,这就及其有利于我们进行 DP。把球和洞摆成一排并排序,设\(f[i][i]\)表示,当 \(j>0\)时表示剩下确定选下了 \(-j\)个洞待匹配的最小代价;当 \(j<0\)时表示 DP 到了第 \(i\) 个位置、剩下多少个球还待匹配的、费用提前计算的代价(提前减去绝对值函数的部分呀),那么当前位置是个球的时候,状态转移方程是:

\[f(i,j)=f(i-1,j+1)+\left\{
\begin{aligned}
-pos_i \ (j>0)\\
pos_i (else)
\end{aligned}
\right.\]

当目前位置是个洞的时候,状态转移方程就是:

\[f(i,j)=min(f(i-1,j),f(i-1,j-1)+\left\{
\begin{aligned}
-pos_i \ (j<0)\\
pos_i (else)
\end{aligned})
\right.\]

然后把第二个式子分析一下,发现当且仅当\(j=0\)的时候需要取 min, 其他时候只需要直接取右手边的作为最小值即可,所以我们把 DP 数组切成三段,下标是负的一段,0一段,正的一段,就可以实现 \(\Theta(n)\)。评测记录

总结:有的时候转移的复杂度比较大/状态的维度比较大的时候,我们不妨退而求其次,通过把状态升一维的代价把转移降低一维,也许就可以通过队列优化,反而能优化掉一维。

一类利用队列优化的DP的更多相关文章

  1. hdu-3401-Trade-单调队列优化的DP

    单调队列入门题... dp[i][j]:第i天.手中拥有j个股票时,获得的最大利润. 若第i天不买不卖:dp[i][j]=max(dp[i][j],dp[i-1][j]); 若第i天买         ...

  2. 【NOIP2017】跳房子 题解(单调队列优化线性DP)

    前言:把鸽了1个月的博客补上 ----------------- 题目链接 题目大意:机器人的灵敏性为$d$.每次可以花费$g$个金币来改造机器人,那么机器人向右跳的范围为$[min(d-g,1),m ...

  3. Trade-----HDU3401----单调队列优化的DP

    题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=3401 题目意思: 有T天,你每天可以以API买进,BPI卖出,最多买ASI个,最多卖BSI个 最多只能 ...

  4. poj1821 Fence【队列优化线性DP】

    Fence Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 6122   Accepted: 1972 Description ...

  5. 洛谷p1725 露琪诺 单调队列优化的DP

    #include <iostream> #include <cstdio> #include <cstring> using namespace std; int ...

  6. BestCoder Round #89 02单调队列优化dp

    1.BestCoder Round #89 2.总结:4个题,只能做A.B,全都靠hack上分.. 01  HDU 5944   水 1.题意:一个字符串,求有多少组字符y,r,x的下标能组成等比数列 ...

  7. 单调队列以及单调队列优化DP

    单调队列定义: 其实单调队列就是一种队列内的元素有单调性的队列,因为其单调性所以经常会被用来维护区间最值或者降低DP的维数已达到降维来减少空间及时间的目的. 单调队列的一般应用: 1.维护区间最值 2 ...

  8. bzoj1855: [Scoi2010]股票交易 单调队列优化dp ||HDU 3401

    这道题就是典型的单调队列优化dp了 很明显状态转移的方式有三种 1.前一天不买不卖: dp[i][j]=max(dp[i-1][j],dp[i][j]) 2.前i-W-1天买进一些股: dp[i][j ...

  9. 队列优化和斜率优化的dp

    可以用队列优化或斜率优化的dp这一类的问题为 1D/1D一类问题 即状态数是O(n),决策数也是O(n) 单调队列优化 我们来看这样一个问题:一个含有n项的数列(n<=2000000),求出每一 ...

随机推荐

  1. mac无坑安装nginx

    mac无坑安装nginx 首先需要mac下有一个缺失的软件包的管理器------->homebrew 1.打开终端输入 brew update 说明homebrew已经安装好了 2.继续执行以下 ...

  2. [BUAA]起点 软工第一次作业-热身

    项目 内容 这个作业属于哪个课程 2020计算机学院软件工程(罗杰 任健) 这个作业的要求在哪里 第一次作业-热身! 我在这个课程的目标是 了解软件开发,提高自己的工程能力和团队协作能力 这个作业在哪 ...

  3. 并发编程从零开始(六)-BlockingDeque+CopyOnWrite

    并发编程从零开始(六)-BlockingDeque+CopyOnWrite 5.2 BlockingDeque BlockingDeque定义了一个阻塞的双端队列接口: 该接口继承了BlockingQ ...

  4. Noip模拟12 2021.7.12

    T1 interval 亏得昨天晚上改掉了T3并且理解了单调栈,今天一扫这题目就知道要用啥了. 先预处理出以a[i]为最大值的最大左右区间.然后再将a[i]取%!!!是的,要不然会影响单调栈的使用.. ...

  5. STM32单片机的学习方法(方法大体适用所有开发版入门)

    1,一款实用的开发板. 这个是实验的基础,有时候软件仿真通过了,在板上并不一定能跑起来,而且有个开发板在手,什么东西都可以直观的看到,效果不是仿真能比的.但开发板不宜多,多了的话连自己都不知道该学哪个 ...

  6. 『学了就忘』Linux基础 — 7、补充:安装Linxu系统时设置硬盘挂载说明

    目录 (1)新建一个/home分区 (2)再创建一个/boot分区. (3)创建一个swap分区 (4)最后剩余的空间全部分给根目录 (5)总结 上一篇在VMwar虚拟机中安装Linux操作系统中ht ...

  7. 数组中出现次数超过一半的数字 牛客网 剑指Offer

    数组中出现次数超过一半的数字 牛客网 剑指Offer 题目描述 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字.例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}.由于数字 ...

  8. Python3 装逼神器---词云(wordcloud)

    词云 (Word Cloud)是对文本中出现频率较高的词语给予视觉化展示的图形, 是一种常见的文本挖掘的方法. 实例:     依赖包: # pip3 install wordcloud  jieba ...

  9. Spring源码学习之容器的基本实现(一)

    前言 最近学习了<<Spring源码深度解析>>受益匪浅,本博客是对学习内容的一个总结.分享,方便日后自己复习或与一同学习的小伙伴一起探讨之用. 建议与源码配合使用,效果更嘉, ...

  10. OOP作业总结一

    PS:建议用 Edge 查看此博客,Chrome 的话文章界面会有点窄,看起来可能会比较难受,我想改宽点但是不会改. 我会改了!改宽了一些,现在看起来舒服了很多,芜湖. 问题数据已修复,我们胜利辣! ...