dp的优化(单队,斜率)

1.单调队列优化dp

维护最小值:xq.tail()

维护最大值:xq.tail()

其实原理不难,当dp的转移源头是一个区间时,往往使用单调队列来维护区间最值(一般队列里装下标以方便维护区间大小,但也只是一般情况),节省了处理区间的时间(甚至噶掉一维),重点是对区间的处理方式和细节,以及处理什么区间都因题而异,因此直接结合相关情景来进行学习

P2564 [SCOI2009] 生日礼物(例题)

(正片开始)在某两个城市之间有 n座烽火台,每个烽火台都有一个代价。要求连续 m个烽火台中至少要有一个发出信号。求总共最少的代价

dp[i]表示1i中选了i的方案

那么为了合法,只能从[im,i1]中转移

用单调队列优化,最简单的一类

P1725 琪露诺 很像

类似的还有最大子序和,就是拿前缀和搞(记得提前往队列里塞一个0

P2627 [USACO11OPEN] Mowing the Lawn G

FJ有N只排成一排的奶牛,奶牛i的效率为Ei

计算没有连续的超过K只奶牛时可以得到的最大效率。

我们可以考虑从反面下手,找出不选的奶牛,将原来的序列分割成若干长度k的区间

因此定义dp[i][1/0]表示选/不选第i头奶牛时的最大效率

如果不选,那么继承前一位的方案

dp[i][0]=max(dp[i1][0],dp[i1][1])

如果选了,那么在[ik,i]中必须有一个不选

dp[i][1]=max(dp[j][0]+k=j+1iEk),j[ik,i1]

拿前缀和优化:

dp[i][1]=max(dp[j][0]+sum[i]sum[j])

对于枚举的jsum[i]是固定的,那么把他弄出去

dp[i][1]=max(dp[j][0]sum[j])+sum[i]

用单调队列维护dp[j][0]sum[j]即可

单调队列优化多重背包

一个初学背包时的禁忌领域

先摆出多重背包的基本递推式

dp[n]=max(dp[n],dp[nk×v]+k×w),k[1,s],k×vn

发现每次都是多减去一个v,因此设n=pv+q,那么

dp[n]=max(dp[n],dp[i×v+q]),i[0,min(s,p)]

也就是说,dp[pv+q]只会从dp[i×v+q]转移而来

后面的部分可以考虑使用单调队列,但别着急,还有w呢,对于不同的iw的系数也不一样

举个例子:

dp[3v+j]=max(dp[j]+3w,dp[j+v]+2w,dp[j+3w])

考虑使用上一道题的方法,提出一个3w

dp[3v+j]=max(dp[j],dp[j+v]w,dp[j+2v]2w,dp[j+3v]3w)

这样队列维护的就是dp[iv+j]iw,i[0,min(s,p)]

wc太牛逼了

P3423 [POI2005] BAN-Bank Notes

n 种面值的硬币,为 b1,b2,,bn。每种硬币有数量限制ci,现在我们想要凑出面值 m,求最少要用多少个硬币

dp[i]表示凑出金额i所需的最小硬币数

转移:dp[i]=min(dp[ijb[l]]+j)

依照上述思路:维护dp[r+kb[i]]k

一定要初始化啊啊啊啊啊啊啊

P2254 [NOI2005] 瑰丽华尔兹

描述略

第一想到理想的正方形

用两个单调队列维护两个维度

但不需要

这道题大抵是要用到dp

dp[i][j]表示走到该格子的最大长

设一个方向上的时间区间长度为k

则(以向右为例)

dp[i][j]=max(dp[i][l]+jl+1),l[jk+1,j1]

dp[i][j]=max(dp[i][l]l)+j+1,l[jk+1,j1]

其他方向从略(向左/向上时,l[j+1,j+k1],所以是dp[i][l]/dp[l][i]+l,最后j+1

维护:dp[i][l]+orl

那就对于每次滑动,用单调队列这个窗口在每一行/列按这个方向滑动,当然会有无效操作,但没关系,因为顺序是对的,最终答案涉及的方格总能按照正确的顺序被修改和记录,所以无所谓,搞就完了

但是:

对于区间[l,r]rl+1的计算方式包含两个端点,而观察样例可知:每次滑动的起点是不计入答案的

所以操作区间没有加一

方程:

dp[i][j]=max(dp[i][l]+jl),l[jk+1,j1]

dp[i][j]=max(dp[i][l]l)+j,l[jk+1,j1]

踢队头时的区间也是|q.front()l|

md坑害我一天

P3089 [USACO13NOV] Pogo-Cow S

N个目标点,目标点ixi,该点得分为pi。开始时可以选择站在一个目标点上,只允许朝一个方向跳跃,且每次跳跃的距离大于等于上一次跳跃的距离,并且必须跳到一个目标点,求所经目标点的最大得分和

首先,排序,方向未知,所以向左向各跑一遍

定义dp[i][j]表示跳到了目标点i,并且是从j跳到i的最大和,得到关系:

dp[i][j]=max(dp[j][k])+pi,|xjxk||xixj|

dp[i][i]=pi

先以向左为例,向右同理

那么0k<j<in

暴力是O(n3)的,需要优化

肯定优化掉k那一维

我们采用单调队列的思想,既然已经按位置排了序,那么标号越小的点离j越远。利用这个天然的单调,从j开始倒着走找转移点,距离一旦不符合就不找了,节省时间

还有一个大坑:先枚举j,再枚举i

如果不这样,其实相当于默认了起点是1

观察方程,可知每次转移相当于该方案继承了上一次跳跃的起点,所以必须先锁死起点,再去枚举终点

可见,很多时候单调队列的应用就是单调性的应用,从单调性入手遍历,可以少遍历很多非法情况,有时甚至不用建队,用单调性枚举即可

P2569 [SCOI2010] 股票交易

看到APiBPi,知道了同一天内要么买,要么卖,不可能同时进行(否则必亏)

定义dpi,j表示第i天有j张股票时最多能赚多少

分类讨论:

    • 在上一次交易的基础上买进
    • 白手起家(这天手头啥也没有)
    • 在上一次交易的基础上卖出
  • 啥也不干

四大情况,我们来一一分析

  • 在上一次交易的基础上买进

dpi,j=max(dpi,j,dpiW1,jkk×APi),k[1,ASi]

  • 白手起家:相当于直接赋值

dpi,j=APi×k,k[1,ASi]

  • 在上一次的基础上卖出

dpi,j=max(dpi,j,dpiW1,j+k+k×BPi),k[1,BSi]

  • 啥也不干:dpi,j=max(dpi,j,dpi1,j)

暴力O(T×MAXP×(ASi+BSi)),,三次方级别,70pts

考虑使用单调队列优化

根据经验,肯定是维护一个dpi+xi的结构,但当前的状态定义没法子这样做(j+k,kjk,k都不好合并),我们改一下状态,以买进为例:

dpi,j=max(dpi,j,dpiW1,k(jk)×APi),0jkASi

dpi,j=max(dpiW1,k+k×APi)j×APi,

此时k,k对应,括号内的东西在遍历j时可以使用单调队列维护,以单调性维护队尾,以jk的范围维护队头,能压到O(T×MAXP)平方级别

可见状态的定义也非常重要 (服了)

2.斜率优化dp

可以认为是单调队列的拓展方法

观察发现,能用单调队列维护的dp方程,一定能分离出一个与当前枚举的i无关的量(单调队列的维护对象),但如果出现了形如(ij)2=i22ij+j2的东西时,2ij这个东西脚踏双船,ij有一个变了他就变,无法使用普通队列来维护了

那么就针对他来下手

n个数,每次取出一段数[l,r]的花费是(s[r]s[l1])2+MM一定,求最小花费

dp[i]表示取完1i的最小花费

可得:dp[i]=min(dp[j]+(s[i]s[j])2+M),j[1,i)

出现了麻烦项,如何处理

我们把式子这样写:

dp[i]=min(dp[j]+s[j]22s[i]s[j])+s[i]2+M

dp[i]=min(2s[i](s[j])+dp[j]+s[j]2)+s[i]2+M

发现括号里是一个形如kx+b的式子,设ki=2s[i],xj=s[j],bj=dp[j]+s[j]2

对于固定的i,ki是不变的,变得只有xj,bj,而我们发现这两项只与j有关,似乎能用队列了

接下来看如何单调

如果想搞单调,即从j转移比从k转移更优,那么

dp[j]+(s[i]s[j])2+M<dp[k]+(s[i]s[k])2+M

化简得:

dp[j]+s[j]2dp[k]s[k]2s[j]s[k]<2s[i]=ki

f[x]=dp[x]+s[x]2,则

f[j]f[k]s[j]s[k]<ki

看到左边的形式想到小学的直线斜率

那么我们就以(s[i],f[i])为点,代表该处状态

设现在枚举到了t,有i,j,k三个备选状态,且状态如下

显然

f[j]f[k]s[j]s[k]>f[i]f[j]s[i]s[j]

对于2s[t]=kt,有以下几种情况

  • f[j]f[k]s[j]s[k]>f[i]f[j]s[i]s[j]>kt,那么ji优,kj

  • f[j]f[k]s[j]s[k]>kt>f[i]f[j]s[i]s[j],那么ij优,kj

  • kt>f[j]f[k]s[j]s[k]>f[i]f[j]s[i]s[j],那么ij优,jk

也就是说,上凸的点一定不会是最优解,删掉上凸点后,剩下的点一定都往下凸,也就是一个下凸包,即所有线斜率单调不降

接下来,kt已知了,也就说待求直线的斜率已知,那么这根线与凸包最外侧的交点将是转移点(相关详情参考线性规划)

来看看交点的特征

显然,若ka<kb<<ks<kt<,则s点的状态就是转移点

那么用单调队列维护斜率,找到每个i对应的s,转移即可

s:取出队首的两个元素a,b,拿推出的式子比,小的为答案,大的就滚蛋

注:先维护队头更答案,再维护队尾塞进i

P3195 [HNOI2008] 玩具装箱

题目描述

P 教授有编号为 1nn 件玩具,第 i 件玩具经过压缩后的一维长度为 Ci。P 教授不关心容器的数目,他可以制作出任意长度的容器

要求:

  • 在一个一维容器中的玩具编号是连续的。

  • 如果将第 i 件玩具到第 j 个玩具放到一个容器中,那么容器的长度将为 x=ji+k=ijCk

如果容器长度为 x,其制作费用为 (xL)2。其中 L 是一个常量。但他希望所有容器的总费用最小。

dp[i]表示前i个玩具装箱的最小代价,那么

dp[i]=min0j<i(dp[j]+(k=j+1iCk+ij1L)2)

前缀和助阵

dp[i]=min0j<i(dp[j]+(s[i]s[j]+ij1L)2)

再设f[i]=s[i]+i(可预处理)

dp[i]=min0j<i(dp[j]+(f[i]f[j]1L)2)

按鞋油推:

dpj+(fj+L+1)2dpk(fk+L+1)2fjfk<2fi

hi=dpi+(fi+L+1)2

hjhkfjfk<2fi

上板子就好了

[APIO2010] 特别行动队

dpi表示1i的士兵经修正后的最大战力

那么

dp[i]=max0j<i(dp[j]+a(s[i]s[j])2+b(s[i]s[j])+c)

再套:

dpj+asj2bsj(dpk+ask2bsk)sjsk>2asi

fi=dpi+asi2bsi

fjfksjsk>2asi

发现这次是大于号,采用类似的方法,假设是下凸包,发现下凸点是无法成为最优解的,所以这次维护的是上凸包

上板子

P2120 [ZJOI2007] 仓库建设

dp[i]表示在i处建厂的最小费用

对于0j<i,由定义的状态,可知j处已经建厂,再结合题目所说,只有j+1i的货物会流入仓库i,所以

dpi=min0j<i(dpj+k=j+1ipk(xixk))+ci

考虑处理k=j+1ipk(xixk)

k=j+1ipk(xixk)=xik=j+1ipkk=j+1ipkxk

k=j+1ipkk=j+1ipkxk用前缀和处理,记为sisj,titj

dpi=min0j<i(dpj+xi(sisj)+titj)+ci

dpi=min0j<i(dpjxisjtj)+xisi+ti+ci

维护dpjxisjtj

套板子

dpjtj(dpktk)sjsk<xi

fi=dpiti

fjfksjsk<xi

搞就完了

但这题有一堆细(da)节(bian):

  • 若序列中间存在pi=0,会导致除数为零,要return 极大/极小值来代替

  • 中间的工厂没货的话就不可能成为仓库,仓库应该建在最后一个有货的工厂

  • 末尾可能会有若干没货的工厂,要从中取一个最小值

P2900 [USACO08MAR] Land Acquisition G

设长为xi,宽为yi

我们先按x从小到大排序,再让y从大到小(保证每次向前枚举的时候符合题意),同时去掉那些被大土地完全包含的小土地

dpi表示前i块土地的最小花费,则

dpi=min1j<i(dpj+xi×yj+1)

dpjdpkyj+1yk+1<xi

小于号,下凸包?

发现这次的右边成为了一个负数,是单调减的,就好像是这样:

上凸包 (因为6翻了)

可见任何部分的单调性都对凸包是有影响的

还有一点就是队列内元素个数的问题,考虑到一个点就动用了k,k+1两个,所以至少留三个(重一个)

P2365 任务安排

dpi表示前i个任务的最短完成时间

dpi=min(dpj+(sumti+num×s)×(sumfisumfj)),j[0,i1]

sumt,sumf为时间,费用的前缀和,num表示分组数

看到式子里有个num,考虑把它搞掉(事实上,这个东西不能随着方程转移)

化简式子

dpi=min(dpj+sumti×(sumfisumfj)+num×s×(sumfisumfj))

考虑搞掉num×s×(sumfisumfj)

根据dp的尿性,这个num也不符合dp的风格(累计),所以考虑对于已知的分组,把该状态分组的影响全部并到此时的方程里一并计算,就可以秒掉num

方程中已知的分组就是j+1i


如图明显可得:分组后时间上多了一个si+1处)

代价增加(sumfnsumfi)×s

但考虑到num还包含了这一次(j+1处)的启动,所以影响其实是

(sumfisumfj)s+(sumfnsumfi)s=(sumfnsumfj)s

其实就是相对于j+1n不分组(一次也不启动)的增加值

所以

dpi=min(dpj+sumti×(sumfisumfj)+(sumfnsumfj)×s)

O(n2),可过弱化版

接下来到了激动人心的优化环节

dpi=min(dpj(sumti+s)×sumfj)+sumti×sumfi+sumfn×s

dpjdpksumfjsumfk<sumti+s

下凸包

然后就来了个不要脸的

P5785 [SDOI2012] 任务安排ProMax

|Ti|?

wc有负数?

sumt单调性不存在了

dp不变,下凸包不变,麻烦就麻烦在没有单调性

也就是说:

  • 弹掉对头的依据没有了

对于第一种情况,我们就不能弹掉队头了,答案也不一定是队头了,要二分查找了

物理学不存在了!(悲伤)(扭曲)(猴叫)(爬行)(蛇了)

posted @   why?123  阅读(26)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示