单调队列优化动态规划
前置知识:单调队列
1.1例题
例题1:琪露诺
常见思路:首先容易推出朴素转移方程:
令
容易发现
操作:①令
②遍历到一个新的
③先更新队尾:将在新元素(指能转移到当前
④再更新队头:如果队头超出长度限制,从队头出队。
⑤这时已经保证了单调队列中有
时间复杂度:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,l,r,f[200010],a[200010],q[200010],ans=-0x7fffffff;
int main(){
ios::sync_with_stdio(0);
cin>>n>>l>>r;
for(int i=0;i<=n;i++)cin>>a[i];
for(int i=1;i<=n;i++)f[i]=-0x7fffffff;
for(int i=l,h=1,t=0;i<=n;i++){
while(f[q[t]]<=f[i-l]&&h<=t)t--;
q[++t]=i-l;
while(q[h]+r<i)h++;
f[i]=f[q[h]]+a[i];
if(i+r>n)ans=max(ans,f[i]);
}
cout<<ans<<'\n';
return 0;
}
例题2:[NOIP2017 普及组] 跳房子
由于题目要求满足某个条件至少要花多少金币,而且容易发现
如何判断花了
考虑用动态规划,可以令
动态规划的时间复杂度为
操作:①令
②遍历到一个新的
③先更新队尾:将在新元素(指能转移到当前 while
套while
,将所有新元素判断并入队。
④再更新队头:如果队头超出长度限制,从队头出队。
⑤这时已经保证了单调队列中有
动态规划的时间复杂度:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll l=0,r=0,s=0,n,d,k,f[500010],q[500010],a[500010],x[500010],ans=0;
bool ch(ll p){
memset(f,-0x3f3f3f3f3f3f3f3f,sizeof(f));
memset(q,0,sizeof(q));
f[0]=0;
ll res=-0x3f3f3f3f3f3f3f3f;
for(ll i=1,j=0,h=1,t=0;i<=n;i++){
while(j<i&&x[i]-x[j]>=d-p){
while(h<=t&&f[q[t]]<=f[j])t--;
q[++t]=j++;
}
while(h<=t&&x[i]-x[q[h]]>d+p)h++;
if(h<=t)f[i]=f[q[h]]+a[i];
res=max(res,f[i]);
}
return res>=k;
}
int main(){
cin>>n>>d>>k;
for(ll i=1;i<=n;i++){
cin>>x[i]>>a[i];
if(a[i]>0)s+=a[i];
r=max(r,x[i]);
}
if(s<k){cout<<-1<<'\n';return 0;}
while(l<=r){
ll mid=(l+r)>>1;
if(ch(mid))ans=mid,r=mid-1;
else l=mid+1;
}
cout<<ans<<'\n';
return 0;
}
例题3:P6040
这一题有些复杂,但是看完题后还是可以列出转移方程:令
这里好像让人无从下手,但是我们充分发扬乱搞推式子精神,拆开发现式子等同于
注意,这里之所以先h++
是因为在所有操作开始前已经将
点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
inline int read(){
int x=0,flag=1;char c;
while((c=getchar())<'0'||c>'9')if(c=='-')flag=0;
while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
return flag?x:-x;
}
ll n,k,d,x,tp,Seed,h=1,t=0,q[10000010],f[10000010];
inline int rnd(){
static const int MOD = 1e9;
return Seed = ( 1LL * Seed * 0x66CCFF % MOD + 20120712 ) % MOD;
}
int main(){
n=read();k=read();d=read();x=read();tp=read();
if(tp){
Seed=read();
f[1]=rnd();q[++t]=1;
for(ll i=2;i<=n;i++){
ll qwq=rnd();
while(h<=t&&q[h]<i-x)h++;
f[i]=f[q[h]]+k+(i-q[h]-1)*d+qwq;
while(h<=t&&f[q[t]]-q[t]*d>=f[i]-i*d)t--;
q[++t]=i;
}
cout<<f[n]<<'\n';
}else{
f[1]=read();q[++t]=1;
for(ll i=2;i<=n;i++){
ll qwq=read();
while(h<=t&&q[h]<i-x)h++;
f[i]=f[q[h]]+k+(i-q[h]-1)*d+qwq;
while(h<=t&&f[q[t]]-q[t]*d>=f[i]-i*d)t--;
q[++t]=i;
}
cout<<f[n]<<'\n';
}
return 0;
}
总结:单调队列优化动态规划适用于式子可以化为
有三个步骤:入队、出队、转移。
1.2 习题
咕咕咕
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!