单调队列优化dp & 斜率优化dp

Chen_jr's blog

ko no SB da!!!

单调队列优化dp

对于方程

f[i]=min(f[j]+b[j]+a[i]) i-m<j<i

其中a,b为只与i和j有关的函数

显然可以转化为

f[i]=min(f[j]+b[j])+a[i] i-m<j<i

可以考虑用单调队列优化

以min为例,维护一个单调递增的队列,即队首是最小值,与单纯的单调队列用法相同。

for(int i=1;i<=n;++i){
    while(!q.empty() && q.front()<=i-m)q.pop_front();//弹出过期元素
    int j=q.front();
    f[i]=f[j]+b[j]+a[i];
    while(!q.empty() && f[q.back()]+b[q.back()]>f[i]+b[i])q.pop_back();//维护单调性
    q.push_back(i);
}

一些题目

LuoguP2569股票交易

LuoguP2254瑰丽华尔兹

[USACO13NOV]Pogo-Cow S

斜率优化dp

单调队列优化dp中的项至于i(当前状态),或j(决策点)有关,斜率优化dp则一般与 i*j 有关

对于方程

f[i]=min(a[i]+b[j]+c[i]*d[j]) j<i

任取决策点k,j (0<k<j<i)

如果j优于k,则有

a[i]+b[k]+c[i]*d[k]>a[i]+b[j]+c[i]*d[j]

两边消去ai,并移项得

c[i]*(d[k]-d[j])>b[j]-b[k]

假设d单调递增(题设),即 dk<dj

左右同时除以 dkdj

c[i]<(b[j]-b[k])/(d[k]-d[j])

由于ci单调递增(题设),如果此时j不如k,以后也一定不如k,那么就可以把j舍弃,用单调队列实现

左右同时乘1

-c[i]>(b[k]-b[j])/(d[k]-d[j])

Yi=bi,Xi=di,不等式右侧可以看作一个斜率式,令slope(i,j)=(Y(i)Y(j))/(X(i)X(j)),则如果要把队首舍弃,则要满足

-c[i]>slope(q[head],q[head+1])

一定要清楚slope是如何定义的

另外,为了避免出现分母为零的情况,要满足head<tail

考虑维护队列的单调性

对于相邻的三项 j1,j2,j3 ,如果斜率单调递减,则有 slope(j1,j2)>slope(j2,j3),假设 ci>slope(j1,j2),那么一定有 ci>slope(j2,j3),只要有元素出队那么队列会清空(维护了个寂寞),所以要满足斜率单调递增(视情况而定)

head=1,tail=1;
for(int i=1;i<=n;++i){
    while(head<tail && -c[i]>slope(q[head],q[head+1]))head++;
    int j=q[head];
    f[i]=a[i]+b[j]+c[i]*d[j];
    while(head<tail && slope(q[tail-1],q[tail])>slope(q[tail],i))tail--;
    q[++tail]=i;
}

还有一种几何意义上的理解方法,真正的用直线的斜率来证明,队列维护出来的是一个下凸包
(本蒟蒻并不会)

一些题目

P3628 [APIO2010]特别行动队

P3195 [HNOI2008]玩具装箱

P2900 [USACO08MAR]Land Acquisition G

posted @   Chano_sb  阅读(32)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 【.NET】调用本地 Deepseek 模型
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
点击右上角即可分享
微信分享提示