单调队列
1.适用情况
需要快速求出区间最大/最小值的情况(常用于dp优化)/有单调的题
2.原理/思路
每次有元素进队时,找到合适的队尾接上,保证队列单调性。(删除后面不必要的一段)同时,维护可用区间,及时去头。
3.性质
一般,在动态规划的过程中,单调队列中每个元素一般存储的是两个值:
1.在原数列中的位置(下标)
2.他在动态规划中的状态值(而单调队列则保证这两个值同时单调)
4.模板题
参考博客
有N个数(N<=100000) ,在连续M(M<=N)个数里至少要有一个数被选择,求选出来数的最小总和。
输入
第一行两个整数 N,M
接下来N行 Wi(Wi<=100) 表示第i个数 。
输出
一个整数,最小总和 。
Sample Input
5 3
1
2
5
6
2
Sample Output
4
思路分析
状态枚举到i,当m=4时,需要在i-3到i-1中找到最小的F[j],那么枚举到i+1时,需要在i-2到i中找到最小的F[j]。要寻找最小值的区间向后移动一位,也就是F[i-m+1]的值被抛弃,F[i-1]的值被加入。这里可以用单调队列处理,F[i-1]是插队的数据。F[i-1]有资格插队是因为它更优且更靠近i,比它更差的数将被它取代,保留那些数据没有任何好处。而那些已经不再维护区间之外的就不必再对其进行维护,出队即可。
#include<bits/stdc++.h>
using namespace std;
const int N=100000;
int n,m,head=1,tail=0,ans=2147483647;
int a[N+1],f[N+1],que[N+1];
int main(){
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
scanf("%d",&a[i]);
for (int i=1;i<=n;i++){
while(head<=tail&&f[i-1]<=f[que[tail]])tail--;//当F[i-1]比队尾值更优时把队尾值弹出
que[++tail]=i-1;//把F[i-1]插入,这里插入下标而不插入值,便于从队头弹出
while(head<=tail&&que[head]<i-m)head++;//不属于区间维护内的数弹出
f[i]=f[que[head]]+a[i];//状态转移
}
for(int i=n;i>n-m;i--)//求出答案
ans=min(ans,f[i]);
printf("%d\n",ans);
return 0;
}
5.序列_题解
问题描述
作为一名火星人,你为了占领地球,需要想方设法使地球人失去信心。现在你获得了一项能力,控制今后n天的天气温度,对于第i天,你能将温度控制在[ai,bi]中任意一个数字,你的目的是使其中某段时间,温度持续不下降,趁此来攻击地球。现在问你最多可以使连续的多少天满足温度不下降。
输入
第一行给出一个整数n,表示你能控制的天数。
接下来n行,第i行给出2个整数ai,bi,表示你能控制的天气范围。保证ai<=bi。
输出
输出一个整数,表示答案。
Sample Input
4
1 3
2 4
1 1
3 4
Sample Input
2
数据范围
对于20%的数据 3<=n<=10;
对于40%的数据 3<=n<=3000;
对于60%的数据 3<=n<=100000;
对于100%的数据 3<=n<=1000000,1<=ai,bi<=100000。
思路分析
由温度要不严格递增想到单调队列。
#include<bits/stdc++.h>
using namespace std;
int n,a[1000010],b[1000010],q[1000010],head=1,tail=1,ans;
inline int read(){
int x=0;char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9'){
x=(x<<3)+(x<<1)+ch-'0';
ch=getchar();
}
return x;
}
int main(){
for(int i=1;i<=n;++i)
a[i]=read(),b[i]=read();
q[1]=1;
int r=2;
for(int i=1;i<=n&&r<=n;i++){
while(head<=tail&&q[head]<i)head++;
if(r<=i)r=i+1,q[++tail]=i;
while(r<=n&&b[r]>=a[q[head]]){
while(head<=tail&&a[q[tail]]<a[r])tail--;
q[++tail]=r++;
}
ans=max(ans,r-i);
}
printf("%d\n",ans);
return 0;
}