单调队列复习

单调队列

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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值